WPF 手动实现 INotifyPropertyChanged 和 ICommand

  • A+
所属分类:.NET技术
摘要

INotifyPropertyChanged接口定义了一个属性改变处理事件,通知客户端这个属性值已经发生改变。


查看 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框架,也是一个学习过程记录。