WPF進階之接口(3):INotifyPropertyChanged,ICommand

INotifiPropertyChanged

1. 做用:向客戶端發出某一屬性值已更改的通知。該接口包含一個PropertyChanged事件成員(MSDN的解釋)
INotifyPropertyChanged 接口用於向客戶端(一般是執行綁定的客戶端)發出某一屬性值已更改的通知。
例如,考慮一個帶有名爲 FirstName 屬性的 Person 對象。若要提供通常性屬性更改通知,則 Person 類型實現NotifyPropertyChanged 接口並在 FirstName 更改時引起 PropertyChanged 事件。若要在將客戶端與數據源進行綁定時發出更改通知,則綁定類型應具備下列任一功能:

A. 實現 INotifyPropertyChanged 接口(首選)。

B. 爲綁定類型的每一個屬性提供更改事件。

2. 使用:

    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

    }

定義一個抽象基類,該類實現了INotifyPropertyChanged接口,全部繼承自ViewModelBase 的類,都將具 有:通知綁定端,後臺屬性發生變化的能力。

     public class MainViewModel : ViewModelBase
    {

        //.............
        private LeagueList dataToShow;
        public LeagueList DataToShow
        {
            get
            {
                if (dataToShow == null)
                    dataToShow = new LeagueList();
                return dataToShow;
            }
            set
            {
                 dataToShow= value;
                OnPropertyChanged("DataToShow");
            }
         }
     }

MainViewModel 繼承BaseViewModel,當屬性值發生變化的時候,即在屬性的set段中調用OnPropertyChanged函數,那麼就能通知UI,綁定數據源發生了變化,UI也會自動更新數據顯示。那麼如何實現綁定呢,看看下面代碼:

<Window x:Class="Views.Window"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ViewModel="clr-namespace:ViewModels"
    xmlns:View="clr-namespace:Views"
    xmlns:c="clr-namespace:Commands">


   <Window.Resources>
        <DataTemplate DataType="{x:Type ViewModel:MainViewModel}">
            <View:MainView/>
        </DataTemplate>
   </Window.Resources>

上面代碼的意思是,MainViewModel中定義各類數據源和代碼邏輯,如後這些數據按照MainView中所定義的佈局進行顯示,這也就是DataTemplate的做用,這裏不展開(後續將在數據模板中介紹)。

好了,如今把兩個類:MainViewMode(l數據,普通cs文件),MainView(UI,即一個xaml文件,一般該類爲一個Usrcontrol)進行了綁定,那麼具體的數據怎麼實現綁定呢。簡單,看看MainView中的代碼:

<ListBox Grid.Column="0" Name="topiclist" ItemsSource="{Binding Path=DataToShow}">

這樣就實現了,具體數據的綁定。

3. 進一步分析

(1)綁定分析:

首先定義一個抽象基類BaseViewModel實現INotifyProperChanged接口;定義MainViewModel繼承自BaseVIewModel,這樣就能使用PropertyChange函數,當屬性值發生變化的時候,在set段調用PropertyChange函數。

其次,定義好MainView文件,該文件定義界面佈局,實現UI,經過綁定MainViewModel中數據。

最後在Window.xaml中使用DataTemplate將MainViewModel和MainView進行綁定

注意在WPF中,xaml 和xam.cs文件是自動綁定的,可是MainViewModel是普通的cs文件,不是xaml.cs文件,所以,僅僅在MainView中使用綁定,系統不會再MainViewModel中去尋找數據源,,而是在MainView.xaml.cs中去尋找數據。所以須要最後一步。

(2)爲何不在xaml.cs中定義數據源和邏輯代碼?

緣由1:xaml.cs是控件的邏輯文件,而MainViewModel須要繼承INotifyPropertyChange接口,這樣就必須讓控件繼承INotifyPropertyChanged,至關因而控件重寫了,這樣的編程模式,xaml.cs將愈來愈大,這個類的測試也將愈加複雜。所以,從下降類複雜度的角度,不該在xaml.cs中定義數據源和邏輯代碼。

緣由2:若是在xaml.cs中實現邏輯,,不利於邏輯和UI的分離,不利於UI和邏輯的分開編寫,下降的程序編寫的效率,同時增長了代碼的耦合度。採用MainViewModel和 MainView的框架,其實就是MVVM模式(Model-View-ViewModel),該模式能夠說和WPF是珠聯璧合,等我陸續闡述完,各類基礎後,,我將在WPF進階之MVVM中詳細說明,如今請你們,耐心掌握基礎。

(3)看剛纔的例子,ListBox的ItemsSource一般須要一個集合類型數據,好了,咱們知道ObservableCollection是一個數據集合類型,而且實現了INotifyPropertyChanged接口,那麼爲何不綁定到一個ObservableCollection數據類型呢?

緣由:說的很對一般綁定到ObservableCollection是可行的,綁定到普通數據,並實現INotifyPropertyChanged也是可行的,。他們的區別在於ObservableCollection繼承INotifyCollectionChanged, INotifyPropertyChanged那麼當Collection添加項,刪除項,刷新時,都將發送PropertyChanged通知,有時這部分功能是咱們不須要的,所以,採用本身實現INotifyPropertyChanged的類將更具靈活性。


ICommand

定義:

你們知道在xaml中定義的事件,響應函數只能在xaml.cs中,如上所述,若是咱們採用MVVM框架,那麼咱們不能經過事件響應的模式,實現代碼邏輯。那麼如何監聽事件呢。咱們使用命令。且看下面實現

    //不帶參數的命令類型

    public class DelegateCommand : ICommand
    {
         public DelegateCommand(Action executeMethod) : this(executeMethod, null, false)
        {
        }

        public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
            : this(executeMethod, canExecuteMethod, false)        {
        }

        public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
        {
            if (executeMethod == null)
            {
                throw new ArgumentNullException("executeMethod");
            }

            _executeMethod = executeMethod;
            _canExecuteMethod = canExecuteMethod;
            _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
        }

        public bool CanExecute()
        {
            if (_canExecuteMethod != null)
            {
                return _canExecuteMethod();
            }
            return true;
        }

        public void Execute()
        {
            if (_executeMethod != null)
            {
                _executeMethod();
            }
        }

        public bool IsAutomaticRequeryDisabled
        {
            get
            {
                return _isAutomaticRequeryDisabled;
            }
            set
            {
                if (_isAutomaticRequeryDisabled != value)
                {
                    if (value)
                    {
                        CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
                    }
                    else
                    {
                        CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
                    }
                    _isAutomaticRequeryDisabled = value;
                }
            }
        }

        public void RaiseCanExecuteChanged()
        {
            OnCanExecuteChanged();
        }

        protected virtual void OnCanExecuteChanged()
        {
            CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (!_isAutomaticRequeryDisabled)
                {
                    CommandManager.RequerySuggested += value;
                }
                CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
            }
            remove
            {
                if (!_isAutomaticRequeryDisabled)
                {
                    CommandManager.RequerySuggested -= value;
                }
                CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
            }
        }

        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute();
        }

        void ICommand.Execute(object parameter)
        {
            Execute();
        }

        private readonly Action _executeMethod = null;
        private readonly Func<bool> _canExecuteMethod = null;
        private bool _isAutomaticRequeryDisabled = false;
        private List<WeakReference> _canExecuteChangedHandlers;

       }

    public class DelegateCommand<T> : ICommand
    {

        //篇幅限制,不作展開,這裏和前一個DelegateCommand相似,不過是定義一個帶參數的命令
    }


         //採用弱引用,避免內存泄漏

        internal class CommandManagerHelper
    {
        internal static void CallWeakReferenceHandlers(List<WeakReference> handlers)
        {
            if (handlers != null)
            {
                // Take a snapshot of the handlers before we call out to them since the handlers
                // could cause the array to me modified while we are reading it.

                EventHandler[] callees = new EventHandler[handlers.Count];
                int count = 0;

                for (int i = handlers.Count - 1; i >= 0; i--)
                {
                    WeakReference reference = handlers[i];
                    EventHandler handler = reference.Target as EventHandler;
                    if (handler == null)
                    {
                        // Clean up old handlers that have been collected
                        handlers.RemoveAt(i);
                    }
                    else
                    {
                        callees[count] = handler;
                        count++;
                    }
                }

                // Call the handlers that we snapshotted
                for (int i = 0; i < count; i++)
                {
                    EventHandler handler = callees[i];
                    handler(null, EventArgs.Empty);
                }
            }
        }

        internal static void AddHandlersToRequerySuggested(List<WeakReference> handlers)
        {
            if (handlers != null)
            {
                foreach (WeakReference handlerRef in handlers)
                {
                    EventHandler handler = handlerRef.Target as EventHandler;
                    if (handler != null)
                    {
                        CommandManager.RequerySuggested += handler;
                    }
                }
            }
        }

        internal static void RemoveHandlersFromRequerySuggested(List<WeakReference> handlers)
        {
            if (handlers != null)
            {
                foreach (WeakReference handlerRef in handlers)
                {
                    EventHandler handler = handlerRef.Target as EventHandler;
                    if (handler != null)
                    {
                        CommandManager.RequerySuggested -= handler;
                    }
                }
            }
        }

        internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler)
        {
            AddWeakReferenceHandler(ref handlers, handler, -1);
        }

        internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler, int defaultListSize)
        {
            if (handlers == null)
            {
                handlers = (defaultListSize > 0 ? new List<WeakReference>(defaultListSize) : new List<WeakReference>());
            }

            handlers.Add(new WeakReference(handler));
        }

        internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler)
        {
            if (handlers != null)
            {
                for (int i = handlers.Count - 1; i >= 0; i--)
                {
                    WeakReference reference = handlers[i];
                    EventHandler existingHandler = reference.Target as EventHandler;
                    if ((existingHandler == null) || (existingHandler == handler))
                    {
                        // Clean up old handlers that have been collected
                        // in addition to the handler that is to be removed.
                        handlers.RemoveAt(i);
                    }
                }
            }
        }
    }
}

上面的代碼沒必要深究,之後使用多了天然明白,這裏不作展開。

定義一個帶參數命令

       DelegateCommand<object> newsItemLoopCommand;
        public ICommand NewsItemLoopCommand
        {
            get 
            {
                if (newsItemLoopCommand == null)
                {
                    Action<object> exe = new Action<object>(NextOrPreviousNews);
                    newsItemLoopCommand = new DelegateCommand<object>(exe);                   
                }
                return newsItemLoopCommand;
            }
        }    

咱們將Button的Command綁定到此命令,而且傳遞一個參數:

<Button x:Name="BackButton" Command="{Binding Path=ItemLoopCommand}" CommandParameter="Previous"/>

這樣當click Button時,就會執行NextOrPreviousNews函數。

問題:

1. 基本上只有少數控件(如Button)在帶有Command,多數控件沒有Command屬性,怎麼使用命令呢。

咱們知道有些時間發生後,屬性值也會變化,例如ListBox 的SelectionChanged事件發生時,SelectedItem也將產生變化,那麼咱們能夠將SelectedItem綁定一個後臺屬性PropertyItem,當SelectionChanged發生時變化,SelectedItem也變,因爲綁定,PropertyItem也變,所以咱們能夠在PropertyItem的set段中加入邏輯代碼,達到咱們的目的。

2.這裏咱們遺留了幾個問題。MVVM的講解,ICommand實現的講解,弱引用的講解。

ICommand實現的講解看看MSDN便知道,主要是實現CanExcute和Excute兩個函數。

弱引用,我將在下一篇中介紹。

關於MVVM請等慢慢講完全部基礎。包括:控件重寫,數據綁定等內容。

轉自:http://hi.baidu.com/leo_han/item/fe0eec6f54217e0da0cf0f0a編程

相關文章
相關標籤/搜索