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編程