WPF 手动实现 INotifyPropertyChanged 和 ICommand
查看 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框架,也是一个学习过程记录。