WPF 手動實現 INotifyPropertyChanged 和 ICommand
阿新 • • 發佈:2021-01-12
檢視 INotifyPropertyChanged 介面原始碼
namespace System.ComponentModel { // // 摘要: // Notifies clients that a property value has changed. public interface INotifyPropertyChanged { // // 摘要: // Occurs when a property value changes. event PropertyChangedEventHandler PropertyChanged; } }
INotifyPropertyChanged介面定義了一個屬性改變處理事件,通知客戶端這個屬性值已經發生改變。
定義NotifyObject實現INotifyPropertyChanged
public class NotifyObject : INotifyPropertyChanged { /// <summary> /// Occurs when a property value changes. /// </summary> public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// Checks if a property already matches a desired value. Sets the property and /// notifies listeners only when necessary. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="storage"></param> /// <param name="value"></param> /// <param name="propertyName"></param> /// <returns></returns> protected virtual bool SetProperty<T>(ref T storage,T value,[CallerMemberName] string propertyName=null) { if (EqualityComparer<T>.Default.Equals(storage, value)) return false; storage = value; RaisePropertyChanged(propertyName); return true; } /// <summary> /// Raises this object's PropertyChanged event. /// </summary> /// <param name="propertyName"></param> protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
通過SetProperty<T>泛型方法 可以接收任意型別的屬性,然後判斷屬性值是否發生變化,如果變化就觸發PropertyChanged事件,通知UI本屬性值已經發生改變。
檢視ICommand原始碼
namespace System.Windows.Input { // // 摘要: // Defines a command. public interface ICommand { // // 摘要: // Occurs when changes occur that affect whether or not the command should execute. event EventHandler CanExecuteChanged; // // 摘要: // Defines the method that determines whether the command can execute in its current // state. // // 引數: // parameter: // Data used by the command. If the command does not require data to be passed, // this object can be set to null. // // 返回結果: // true if this command can be executed; otherwise, false. bool CanExecute(object parameter); // // 摘要: // Defines the method to be called when the command is invoked. // // 引數: // parameter: // Data used by the command. If the command does not require data to be passed, // this object can be set to null. void Execute(object parameter); } }
ICommand介面定義了一個普通的事件,和命令執行方法Execute()、命令是否可以執行方法CanExecute()
定義DelegateCommand實現 ICommand
public class DelegateCommand : ICommand { public event EventHandler CanExecuteChanged; private readonly Action _executeMethod; private readonly Func<bool> _canExecuteMethod; /// <summary> /// Creates a new instance of DelegateCommand with the Action to invoke on execution. /// </summary> /// <param name="executeMethod"></param> public DelegateCommand(Action executeMethod) : this(executeMethod, () => true) { } /// <summary> /// Creates a new instance of DelegateCommand with the Action to invoke on execution /// and a Func to query for determining if the command can execute. /// </summary> /// <param name="executeMethod"></param> /// <param name="canExecuteMethod"></param> public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod) { if(executeMethod==null||canExecuteMethod==null) throw new ArgumentNullException(nameof(executeMethod)); _executeMethod = executeMethod; _canExecuteMethod = canExecuteMethod; } /// <summary> /// Executes the command. /// </summary> /// <param name="parameter"></param> public void Execute(object parameter) { _executeMethod(); } /// <summary> /// Determines if the command can be executed. /// </summary> /// <param name="parameter"></param> /// <returns></returns> public bool CanExecute(object parameter) { return _canExecuteMethod(); } }
通過DelegateCommand建構函式載入兩個委託(Action _executeMethod ,Func<bool> _canExecuteMethod),如果存在可以正常實現命令,這裡ICommand的實現也是極簡模式,後面可以繼續擴充套件。
在ViewModel中使用NotifyObject和DelegateCommand
public class MainWindowViewModel:NotifyObject { /// <summary> /// 輸入1 /// </summary> private double _input1; public double Input1 { get => _input1; set => SetProperty(ref _input1,value); } /// <summary> /// 輸入2 /// </summary> private double _input2; public double Input2 { get => _input2; set => SetProperty(ref _input2, value); } /// <summary> /// 結果 /// </summary> private double _result; public double Result { get => _result; set => SetProperty(ref _result, value); } /// <summary> /// 加法命令 /// </summary> public DelegateCommand _addCommand; public DelegateCommand AddCommand => _addCommand ??= new DelegateCommand(Add); private void Add() { Result = Input1+Input2; } }
在ViewModel 定義Input1 Input2 Result 跟View中的控制元件進行資料繫結,定義AddCommand跟View中事件擁有者繫結(命令繫結),當UI介面點選加法按鈕,事件處理器就會響應這個命令執行Add()方法,完成運算。
總結:
通過上述介面實現,簡單可以實現資料繫結和命令繫結,這個思路主要借鑑Prism框架,也是一個學習過程記錄。