之前基于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 }