Command

命令是实现MVVM的基础之一,代替了click event这种紧耦合。

RoutedUICommand与RoutedCommand的不同

RoutedUICommand继承于RoutedCommand,二者相差Text以及GetText方法

        <Button Command="ApplicationCommands.Copy"></Button>
        <Menu>
            <MenuItem Command="ApplicationCommands.Copy"></MenuItem>
        </Menu>

给Button和MenuItem绑定WPF预定义好的Command,Button的Content不会获取RoutedUICommand的Text,但是MenuItem在Header没有设置情况下,会去获取Command的Text

Command使用方向

  1. 实现ICommandSource的控件使用RoutedCommand,父元素上通过CommandBinding设置Execute和CanExecuted的handler
  2. 继承ICommand实现自己的Command(通过实例讨论自定义Command)
    下面这个例子,CanExecuteChanged和RaiseCanExecuteChanged没搞懂干啥的,回头再解释。但是能用,空间里面通过Binding来关联VM里面的command属性。
    参考其他代码
   public class ActionCommand : ICommand
    {
        readonly Action<object> action;
        readonly Predicate<object> canExecute;
        private EventHandler eventHandler;

        public ActionCommand(Action<object> action) : this(action, null) { }

        public ActionCommand(Action<object> action, Predicate<object> canExecute)
        {
            this.action = action;
            this.canExecute = canExecute;
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                eventHandler += value;
                CommandManager.RequerySuggested += value;
            }
            remove
            {
                eventHandler -= value;
                CommandManager.RequerySuggested -= value;
            }
        }

        public void RaiseCanExecuteChanged()
        {
            eventHandler?.Invoke(this, new EventArgs());
        }

        public bool CanExecute(object parameter)
        {
            return canExecute == null ? true : canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            action(parameter);
        }
    }

如果

        public event EventHandler CanExecuteChanged
        {
            add
            {
                eventHandler += value;
                CommandManager.RequerySuggested += value;
            }
            remove
            {
                eventHandler -= value;
                CommandManager.RequerySuggested -= value;
            }
        }

变成

public event EventHandler CanExecuteChanged;

如果是button的command,那么只有点击Button时候才会trigger CanExecute, 假如在界面移动鼠标,则不会被Trigger

CommandManager.RequerySuggested这是为什么??
routedcommand的CanExecuteChange是什么情况

CommandManager.RequerySuggested += value,对于button,其中的value是什么
Command是button的依赖属性,当button初始化后,PropertyChangedCallback方法onCommandChanged
=>HookCommand
=>CanExecuteChangedEventManage.AddHandler,command,buttonbase.OnCanExecuteChanged)
=>CurrentManager.PrivateAddHandler

  1. 在PrivateAddHandler中,Command和Buttonbase.OnCanExecuteChanged被放入一个Hash表中
  2. Command的CanExecuteChange赋值为Buttonbase.OnCanExecuteChanged,也就是说CommandManager.RequerySuggested添加了Buttonbase.OnCanExecuteChanged
  3. Button的OnCanExecuteChanged里面的逻辑是检查command是否CanExecute
    当UI的变化,会引起Buttonbase.OnCanExecuteChanged被调用,检查command是否canExecute
    CommandManager.RequerySuggested
    是为了强引用,防止被垃圾回收什么底,event真正被调用的地方,是那张hash表,弱引用。
    internal bool DeliverEvent(ref Listener listener, object sender, EventArgs args, Type managerType)这里是调用入buttonbase的分界方法
        [CommonDependencyProperty]
        public static readonly DependencyProperty CommandProperty =
                DependencyProperty.Register(
                        "Command",
                        typeof(ICommand),
                        typeof(ButtonBase),
                        new FrameworkPropertyMetadata((ICommand)null,
                            new PropertyChangedCallback(OnCommandChanged)));

        private void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
        {
            if (oldCommand != null)
            {
                UnhookCommand(oldCommand);
            }
            if (newCommand != null)
            {
                HookCommand(newCommand);
            }
        }

       private void HookCommand(ICommand command)
        {
            CanExecuteChangedEventManager.AddHandler(command, OnCanExecuteChanged);
            UpdateCanExecute();
        }

      public static void AddHandler(ICommand source, EventHandler<EventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (handler == null)
                throw new ArgumentNullException("handler");

            CurrentManager.PrivateAddHandler(source, handler);
        }

      private void PrivateAddHandler(ICommand source, EventHandler<EventArgs> handler)
        {
            // get the list of sinks for this source, creating if necessary
            List<HandlerSink> list = (List<HandlerSink>)this[source];
            if (list == null)
            {
                list = new List<HandlerSink>();
                this[source] = list;
            }

            // add a new sink to the list
            HandlerSink sink = new HandlerSink(this, source, handler);
            list.Add(sink);

            // keep the handler alive
            AddHandlerToCWT(handler, _cwt);
        }
image.png

自定义Command的动机

参考深入浅出的自定义控件

  1. 自定义控件,实现自己的Command,不再是定制好的button,menuitem,ListBoxItem的固定动作event,trigger的command执行
  2. 想使用Target,但控件本身不是InputElement的子类。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容