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 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM