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

 

posted @ 2021-01-12 11:41  NiKaFace  阅读(88)  评论(0编辑  收藏