WPF 4.0中ICommand.CanExecuteChanged事件订阅对象的变化

之前基于WPF 3.0开发的应用程序有一个DelegateCommand类型,在升级至WPF 4.0后发现CanExecuteChanged事件产生通知后对应的UI并未产生变化。

  1 /// <summary>
  2 /// 委托命令
  3 /// </summary>
  4 public class DelegateCommand<T> : ICommand
  5 {
  6     /// <summary>
  7     /// 命令执行前事件
  8     /// </summary>
  9     public event EventHandler<CancelEventArgs> Executing;
 10 
 11     /// <summary>
 12     /// 命令执行后事件
 13     /// </summary>
 14     public event EventHandler Executed;
 15 
 16     private Boolean _canExecuteCache;
 17 
 18     /// <summary>
 19     /// 构造函数
 20     /// </summary>
 21     protected DelegateCommand() { }
 22 
 23     /// <summary>
 24     /// 构造函数
 25     /// </summary>
 26     /// <param name="executeActionFunc">执行函数</param>
 27     public DelegateCommand(Action<T> executeActionFunc)
 28         : this(executeActionFunc, null) { }
 29 
 30     /// <summary>
 31     /// 构造函数
 32     /// </summary>
 33     /// <param name="executeActionFunc">执行函数</param>
 34     /// <param name="canExecuteFunc">可执行函数</param>
 35     public DelegateCommand(Action<T> executeActionFunc, Func<T, Boolean> canExecuteFunc)
 36     {
 37         this.ExecuteActionFunc = executeActionFunc;
 38         this.CanExecuteFunc = canExecuteFunc;
 39     }
 40 
 41     /// <summary>
 42     /// 执行函数
 43     /// </summary>
 44     public Action<T> ExecuteActionFunc { get; protected set; }
 45 
 46     /// <summary>
 47     /// 可执行函数
 48     /// </summary>
 49     public Func<T, Boolean> CanExecuteFunc { get; protected set; }
 50 
 51     #region ICommand Members
 52 
 53     /// <summary>
 54     /// 允许执行变化事件
 55     /// </summary>
 56     public event EventHandler CanExecuteChanged;
 57 
 58     /// <summary>
 59     /// 命令是否可执行
 60     /// </summary>
 61     /// <param name="parameter">参数</param>
 62     /// <returns>是否可执行</returns>
 63     public Boolean CanExecute(Object parameter)
 64     {
 65         if (this.CanExecuteFunc == null)
 66             return true;
 67 
 68         Boolean bResult = this.CanExecuteFunc((T)parameter);
 69 
 70         if (bResult != _canExecuteCache)
 71         {
 72             _canExecuteCache = bResult;
 73 
 74             EventHandler handler = CanExecuteChanged;
 75 
 76             if (handler != null)
 77                 handler(parameter, EventArgs.Empty);
 78         }
 79 
 80         return bResult;
 81     }
 82 
 83     /// <summary>
 84     /// 执行命令
 85     /// </summary>
 86     /// <param name="parameter">参数</param>
 87     public void Execute(Object parameter)
 88     {
 89         CancelEventArgs e = new CancelEventArgs(false);
 90         EventHandler<CancelEventArgs> beforeHandler = Executing;
 91 
 92         if (beforeHandler != null)
 93             beforeHandler(parameter, e);
 94 
 95         if (!e.Cancel)
 96         {
 97             this.ExecuteActionFunc((T)parameter);
 98 
 99             EventHandler afterHandler = Executed;
100 
101             if (afterHandler != null)
102                 afterHandler(parameter, e);
103         }
104     }
105 
106     #endregion
107 }

对于ICommand的绑定,WPF内部会订阅CanExecuteChanged事件,当对应的ICommand实现产生通知时调用CanExecute函数确认是否需要更新控件状态。调用Delegate的GetInvocationList函数发现WPF 3.0返回的Target是对应的控件(比如Button),WPF 4.0则返回CanExecuteChangedEventManager类型。在CanExecuteChangedEventManager内部它需要使用sender关联WeakEventTable获取被派发的ListenerList,而问题代码就在RaiseCanExecuteChanged时传递的sender使用了parameter:

 1 /// <summary>
 2 /// 引起允许执行变化事件
 3 /// </summary>
 4 public void RaiseCanExecuteChanged()
 5 {
 6     EventHandler handler = CanExecuteChanged;
 7 
 8     if (handler != null)
 9         handler(parameter, EventArgs.Empty);
10 }

修改为:

 1 /// <summary>
 2 /// 引起允许执行变化事件
 3 /// </summary>
 4 public void RaiseCanExecuteChanged()
 5 {
 6     EventHandler handler = CanExecuteChanged;
 7 
 8     if (handler != null)
 9         handler(this, EventArgs.Empty);
10 }
原文地址:https://www.cnblogs.com/junchu25/p/2733960.html