2021年1月12日星期二

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

 









原文转载:http://www.shaoqun.com/a/508535.html

跨境电商:https://www.ikjzd.com/

e邮宝:https://www.ikjzd.com/w/594.html?source=tagwish

福茂:https://www.ikjzd.com/w/1633


查看INotifyPropertyChanged接口源码namespaceSystem.ComponentModel{////摘要://Notifiesclientsthatapropertyvaluehaschanged.publicinterfaceINotifyPropertyChanged{////摘要://Occurswhenapropertyvaluechanges.eventProp
anker:anker
mail.ru:mail.ru
亚马逊为消费者提供免费退货选项,扩大免费退货范围!:亚马逊为消费者提供免费退货选项,扩大免费退货范围!
拉萨天气_拉萨天气预报 :拉萨天气_拉萨天气预报
亚马逊站外引流-7步引爆亚马逊流量:亚马逊站外引流-7步引爆亚马逊流量

没有评论:

发表评论