本文主要介紹WinForm項目中如何像WPF一樣優雅的使用Command來實現業務操作。想必大家已經疲於雙擊控件生成事件處理方法來實現業務操作,如多控件(按鈕、菜單、工具條、狀態欄等命令控件)來做同一個動作,還要控制它們啟用(Enabled)狀態等等,使代碼結構冗長且可讀性差。下面具體介紹和實現WinForm中對Command的應用。
- 命令(Command)
顧 名思義,定義一個命令,繼承至System.Windows.Input.ICommand接口,實現 Execute(object) ,CanExecute(object)方法和 CanExecuteChanged事件。由 CanExecute 確定是否調用 Execute 執行該命令。
1 /// <summary> 2 /// 定義一個執行的命令。 3 /// </summary> 4 public abstract class Command : ICommand 5 { 6 /// <summary> 7 /// The can executable 8 /// </summary> 9 private bool canExecutable = true; 10 11 /// <summary> 12 /// 當出現影響是否應執行該命令的更改時發生。 13 /// </summary> 14 public event EventHandler CanExecuteChanged; 15 16 /// <summary> 17 /// 定義用於確定此命令是否可以在其當前狀態下執行的方法。 18 /// </summary> 19 /// <param name="parameter">此命令使用的數據。 如果此命令不需要傳遞數據,則該對象可以設置為 null。</param> 20 /// <returns>如果可以執行此命令,則為 true;否則為 false。</returns> 21 public abstract bool CanExecute(object parameter); 22 23 /// <summary> 24 /// 定義在調用此命令時調用的方法。 25 /// </summary> 26 /// <param name="parameter">此命令使用的數據。 如果此命令不需要傳遞數據,則該對象可以設置為 null。</param> 27 public abstract void Execute(object parameter); 28 29 30 /// <summary> 31 /// 提升命令狀態更改。 32 /// </summary> 33 /// <param name="parameter">The parameter.</param> 34 internal protected void RaiseCommandState(object parameter) 35 { 36 var able = CanExecute(parameter); 37 if (able != canExecutable) 38 { 39 canExecutable = able; 40 OnCanExecuteChanged(EventArgs.Empty); 41 } 42 } 43 44 /// <summary> 45 /// 觸發當命令狀態更改事件。 46 /// </summary> 47 /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> 48 protected virtual void OnCanExecuteChanged(EventArgs e) 49 { 50 if (CanExecuteChanged != null) 51 { 52 CanExecuteChanged(this, e); 53 } 54 } 55 }
- 命令參數(CommandParameter)
提供給命令執行時的參數,該對象主要由參數源(Source)和參數源的屬性(Parameter)構成。參數源可繼承至System.ComponentModels.INotifyPropertyChanged接口,實現屬性值更改通知。此 Parameter 必須為屬性(也可以是靜態屬性)。
1 /// <summary> 2 /// 表示命令參數。 3 /// </summary> 4 public sealed class CommandParameter 5 { 6 private readonly Type sourceType; 7 private readonly object source; 8 private readonly string propertyMember; 9 10 private readonly bool booleanOppose = false; 11 private Command command; 12 13 /// <summary> 14 /// 獲取一個值,該值表示命令參數源。 15 /// </summary> 16 /// <value>The source.</value> 17 public object Source 18 { 19 get { return source; } 20 } 21 22 /// <summary> 23 /// 獲取一個值,該值表示命令參數的類型若當前非靜態類型綁定則為 Source 類型。 24 /// </summary> 25 /// <value>The type of the source.</value> 26 public Type SourceType 27 { 28 get 29 { 30 return sourceType; 31 } 32 } 33 34 /// <summary> 35 /// 獲取一個值,該值表示命令執行參數。 36 /// </summary> 37 /// <value>The parameter.</value> 38 public object Parameter { get { return ResolvePropertyValue(); } } 39 40 /// <summary> 41 /// 獲取一個值,該值表示參數所屬的命令。 42 /// </summary> 43 /// <value>The command.</value> 44 public Command Command 45 { 46 get 47 { 48 return command; 49 } 50 internal set 51 { 52 if (value != command) 53 { 54 command = value; 55 command.RaiseCommandState(Parameter); 56 } 57 } 58 } 59 60 /// <summary> 61 /// 初始化 RelateCommandParameter 新實例。 62 /// </summary> 63 /// <param name="source">綁定源。</param> 64 /// <param name="propertyMember">綁定成員(屬性名稱)。</param> 65 /// <param name="booleanOppose">若值為System.Boolean時,是否取反。</param> 66 public CommandParameter(object source, string propertyMember, bool booleanOppose = false) 67 { 68 this.source = source; 69 this.sourceType = source.GetType(); 70 this.propertyMember = propertyMember; 71 this.booleanOppose = booleanOppose; 72 BindNotifyObject(source); 73 } 74 75 /// <summary> 76 /// 初始化 RelateCommandParameter 新實例。 77 /// </summary> 78 /// <param name="source">綁定源。</param> 79 /// <param name="propertyMember">綁定成員(屬性名稱)。</param> 80 /// <param name="booleanOppose">若值為System.Boolean時,是否取反。</param> 81 public CommandParameter(object source, Expression<Func<string>> propertyMember, bool booleanOppose = false) 82 : this(source, ResolvePropertyName(propertyMember), booleanOppose) 83 { 84 85 } 86 87 /// <summary> 88 /// 初始化一個可指定靜態成員的 RelateCommandParameter 新實例。 89 /// </summary> 90 /// <param name="staticSourceType">靜態類類型。</param> 91 /// <param name="propertyMember">綁定成員(屬性名稱)。</param> 92 /// <param name="booleanOppose">若值為System.Boolean時,是否取反。</param> 93 public CommandParameter(Type staticSourceType, string propertyMember, bool booleanOppose = false) 94 { 95 this.sourceType = staticSourceType; 96 this.propertyMember = propertyMember; 97 this.booleanOppose = booleanOppose; 98 } 99 100 private void BindNotifyObject(object source) 101 { 102 if (typeof(INotifyPropertyChanged).IsAssignableFrom(source.GetType())) 103 { 104 ((INotifyPropertyChanged)source).PropertyChanged += SourcePropertyChanged; 105 } 106 } 107 108 private void SourcePropertyChanged(object sender, PropertyChangedEventArgs e) 109 { 110 if (e.PropertyName == propertyMember) 111 { 112 if (Command != null) 113 { 114 Command.RaiseCommandState(Parameter); 115 } 116 } 117 } 118 119 private object ResolvePropertyValue() 120 { 121 var flags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic; 122 if (source == null) 123 { 124 flags |= System.Reflection.BindingFlags.Static; 125 } 126 else 127 { 128 flags |= System.Reflection.BindingFlags.Instance; 129 } 130 131 var pro = sourceType.GetProperty(propertyMember, flags); 132 if (pro == null) 133 { 134 throw new MemberAccessException(string.Format("Not found {2} member \"{0}\" in \"{1}\"", propertyMember, sourceType, source == null ? "static" : "instance")); 135 } 136 if (Type.GetTypeCode(pro.PropertyType) == TypeCode.Boolean) 137 { 138 if (booleanOppose) 139 { 140 return !((Boolean)pro.GetValue(source)); 141 } 142 } 143 return pro.GetValue(source); 144 } 145 146 private static string ResolvePropertyName(Expression<Func<string>> propertyMember) 147 { 148 if (propertyMember != null) 149 { 150 return ((MemberExpression)propertyMember.Body).Member.Name; 151 } 152 else 153 { 154 throw new ArgumentNullException("propertyMember"); 155 } 156 } 157 }
- 命令綁定(CommandBinding)
將命令和命令參數組合構建成一個綁定對象。
1 /// <summary> 2 /// 定義命令綁定對象。 3 /// </summary> 4 public sealed class CommandBinding 5 { 6 /// <summary> 7 /// 獲取一個值,該值表示命令綁定的對象。 8 /// </summary> 9 /// <value>The command.</value> 10 public Command Command { get; private set; } 11 12 /// <summary> 13 /// 獲取一個值,該值表示命令執行參數。 14 /// </summary> 15 /// <value>The command parameter.</value> 16 public CommandParameter CommandParameter { get; private set; } 17 18 /// <summary> 19 /// 初始化 <see cref="CommandBinding"/> 新實例。 20 /// </summary> 21 /// <param name="command">要綁定的命令。</param> 22 public CommandBinding(Command command) 23 : this(command, null) 24 { 25 26 } 27 28 /// <summary> 29 /// 初始化 <see cref="CommandBinding"/> 新實例。 30 /// </summary> 31 /// <param name="command">要綁定的命令。</param> 32 /// <param name="commandParameter">要綁定的命令參數。</param> 33 public CommandBinding(Command command, CommandParameter commandParameter) 34 { 35 this.Command = command; 36 this.CommandParameter = commandParameter; 37 if (this.CommandParameter != null) 38 { 39 this.CommandParameter.Command = this.Command; 40 } 41 } 42 43 /// <summary> 44 /// 初始化 <see cref="CommandBinding"/> 新實例。 45 /// </summary> 46 /// <param name="command">要綁定的命令。</param> 47 /// <param name="source">要綁定的命令參數的實例。</param> 48 /// <param name="propertyMember">要綁定的命令參數的屬性名稱。</param> 49 /// <param name="booleanOppose">若值為System.Boolean值時,是否取反向值。</param> 50 public CommandBinding(Command command, object source, string propertyMember, bool booleanOppose = false) 51 : this(command, CreateCommandParameter(source, propertyMember, booleanOppose)) 52 { 53 54 } 55 56 /// <summary> 57 /// 初始化 <see cref="CommandBinding"/> 新實例。 58 /// </summary> 59 /// <param name="command">要綁定的命令。</param> 60 /// <param name="staticSourceType">靜態類類型。</param> 61 /// <param name="propertyMember">綁定成員(屬性名稱)。</param> 62 /// <param name="booleanOppose">若值為System.Boolean時,是否取反。</param> 63 public CommandBinding(Command command, Type staticSourceType, string propertyMember, bool booleanOppose = false) 64 : this(command, new CommandParameter(staticSourceType, propertyMember, booleanOppose)) 65 { 66 } 67 68 private static CommandParameter CreateCommandParameter(object source, string propertyMember, bool booleanOppose = false) 69 { 70 return new CommandParameter(source, propertyMember, booleanOppose); 71 } 72 }
- 命令目標(CommandTarget)
此為最終執行命令的目標,構建此對象實例時指定一個綁定(CommandBinding),監視WinForm命令控件(如Control、ToolStripItem)等包含 Click 事件和 Enabled 屬性的對象。
1 /// <summary> 2 /// 表示命令綁定的執行目標。 3 /// </summary> 4 public sealed class CommandTarget : IDisposable 5 { 6 private readonly object target; 7 private bool disposed = false; 8 9 /// <summary> 10 /// 獲取一個值,該值表示目標是否已釋放。 11 /// </summary> 12 /// <value><c>true</c> if disposed; otherwise, <c>false</c>.</value> 13 public bool Disposed { get { return disposed; } } 14 15 /// <summary> 16 /// 獲取一個值,該值表示目標的命令綁定源。 17 /// </summary> 18 /// <value>The command binding.</value> 19 public CommandBinding CommandBinding { get; private set; } 20 21 /// <summary> 22 /// 初始化 CommandTarget 新實例。 23 /// </summary> 24 /// <param name="binding">命令綁定源。</param> 25 private CommandTarget(CommandBinding binding) 26 { 27 CommandBinding = binding; 28 CommandBinding.Command.CanExecuteChanged += CommandStateChanged; 29 } 30 31 32 /// <summary> 33 /// 初始化 CommandTarget 新實例。 34 /// </summary> 35 /// <param name="control">綁定目標。</param> 36 /// <param name="commandBinding">命令綁定源。</param> 37 public CommandTarget(Control control, CommandBinding commandBinding) 38 : this(commandBinding) 39 { 40 target = control; 41 control.Click += OnClick; 42 var parameter = GetParameterValue(); 43 control.Enabled = commandBinding.Command.CanExecute(parameter); 44 } 45 46 /// <summary> 47 /// 初始化 CommandTarget 新實例。 48 /// </summary> 49 /// <param name="item">綁定目標。</param> 50 /// <param name="commandBinding">命令綁定源。</param> 51 public CommandTarget(ToolStripItem item, CommandBinding commandBinding) 52 : this(commandBinding) 53 { 54 target = item; 55 item.Click += OnClick; 56 var parameter = GetParameterValue(); 57 item.Enabled = commandBinding.Command.CanExecute(parameter); 58 } 59 60 61 /// <summary> 62 /// Handles the <see cref="E:Click" /> event. 63 /// </summary> 64 /// <param name="sender">The sender.</param> 65 /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> 66 private void OnClick(object sender, EventArgs e) 67 { 68 object parameter = null; 69 if (CommandBinding.CommandParameter != null) 70 { 71 parameter = CommandBinding.CommandParameter.Parameter; 72 } 73 var command = CommandBinding.Command; 74 if (command.CanExecute(parameter)) 75 { 76 command.Execute(parameter); 77 } 78 } 79 /// <summary> 80 /// Commands the state changed. 81 /// </summary> 82 /// <param name="sender">The sender.</param> 83 /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> 84 private void CommandStateChanged(object sender, EventArgs e) 85 { 86 var property = target.GetType().GetProperty("Enabled"); 87 if (property != null) 88 { 89 var parameter = GetParameterValue(); 90 var enable = CommandBinding.Command.CanExecute(parameter); 91 property.SetValue(target, enable); 92 } 93 } 94 95 private object GetParameterValue() 96 { 97 if (CommandBinding.CommandParameter == null) 98 { 99 return null; 100 } 101 return CommandBinding.CommandParameter.Parameter; 102 } 103 104 /// <summary> 105 /// 執行與釋放或重置非托管資源相關的應用程序定義的任務。 106 /// </summary> 107 public void Dispose() 108 { 109 if (disposed) 110 { 111 return; 112 } 113 disposed = true; 114 CommandBindingManager.Unregister(this); 115 CommandBinding.Command.CanExecuteChanged -= CommandStateChanged; 116 if (target is Control) 117 { 118 ((Control)target).Click -= OnClick; 119 } 120 if (target is ToolStripItem) 121 { 122 ((Control)target).Click -= OnClick; 123 } 124 } 125 }
- 命令綁定管理器(CommandBindingManager)
命令綁定管理器提供命令綁定(CommandBinding)與命令控件(WinForm控件)注冊與反注冊功能。
1 /// <summary> 2 /// 表示命令注冊管理器。 3 /// </summary> 4 public static class CommandBindingManager 5 { 6 private static readonly List<CommandTarget> targets = new List<CommandTarget>(); 7 8 /// <summary> 9 /// 注冊命令道指定的命令控件。 10 /// </summary> 11 /// <param name="control">綁定目標。</param> 12 /// <param name="commandBinding">命令綁定源。</param> 13 /// <returns>返回一個命令目標實例。</returns> 14 public static CommandTarget Register(Control control, CommandBinding commandBinding) 15 { 16 var target = new CommandTarget(control, commandBinding); 17 targets.Add(target); 18 return target; 19 } 20 21 /// <summary> 22 /// 注冊命令道指定的命令控件。 23 /// </summary> 24 /// <param name="stripItem">綁定目標。</param> 25 /// <param name="commandBinding">命令綁定源。</param> 26 /// <returns>返回一個命令目標實例。</returns> 27 public static CommandTarget Register(ToolStripItem stripItem, CommandBinding commandBinding) 28 { 29 var target = new CommandTarget(stripItem, commandBinding); 30 targets.Add(target); 31 return target; 32 } 33 34 /// <summary> 35 /// 注冊命令道指定的命令控件。 36 /// </summary> 37 /// <param name="control">綁定目標。</param> 38 /// <param name="command">綁定命令。</param> 39 /// <returns>返回一個命令目標實例。</returns> 40 public static CommandTarget Register(Control control, Command command) 41 { 42 return Register(control, new CommandBinding(command)); 43 } 44 /// <summary> 45 /// 注冊命令道指定的命令控件。 46 /// </summary> 47 /// <param name="stripItem">綁定目標。</param> 48 /// <param name="command">綁定命令。</param> 49 /// <returns>返回一個命令目標實例。</returns> 50 public static CommandTarget Register(ToolStripItem stripItem, Command command) 51 { 52 return Register(stripItem, new CommandBinding(command)); 53 } 54 55 /// <summary> 56 /// 注冊命令道指定的命令控件。 57 /// </summary> 58 /// <param name="control">綁定目標。</param> 59 /// <param name="command">綁定命令。</param> 60 /// <param name="source">構造命令參數的源。</param> 61 /// <param name="propertyName">構造命令參數的名稱。</param> 62 /// <param name="booleanOppose">若值為System.Boolean值時,是否取反向值。</param> 63 /// <returns>返回一個命令目標實例。</returns> 64 public static CommandTarget Register(Control control, Command command, object source, string propertyName, bool booleanOppose = false) 65 { 66 var commandBinding = new CommandBinding(command, source, propertyName, booleanOppose); 67 return Register(control, commandBinding); 68 } 69 70 /// <summary> 71 /// 注冊命令道指定的命令控件。 72 /// </summary> 73 /// <param name="stripItem">綁定目標。</param> 74 /// <param name="command">綁定命令。</param> 75 /// <param name="source">構造命令參數的源。</param> 76 /// <param name="propertyName">構造命令參數的名稱。</param> 77 /// <param name="booleanOppose">若值為System.Boolean值時,是否取反向值。</param> 78 /// <returns>返回一個命令目標實例。</returns> 79 public static CommandTarget Register(ToolStripItem stripItem, Command command, object source, string propertyName, bool booleanOppose = false) 80 { 81 var commandBinding = new CommandBinding(command, source, propertyName, booleanOppose); 82 return Register(stripItem, commandBinding); 83 } 84 85 /// <summary> 86 /// 注冊命令道指定的命令控件。 87 /// </summary> 88 /// <param name="control">綁定目標。</param> 89 /// <param name="command">綁定命令。</param> 90 /// <param name="staticSourceType">靜態類類型。</param> 91 /// <param name="propertyName">構造命令參數的名稱。</param> 92 /// <param name="booleanOppose">若值為System.Boolean值時,是否取反向值。</param> 93 /// <returns>返回一個命令目標實例。</returns> 94 public static CommandTarget Register(Control control, Command command, Type staticSourceType, string propertyName, bool booleanOppose = false) 95 { 96 var commandBinding = new CommandBinding(command, staticSourceType, propertyName, booleanOppose); 97 return Register(control, commandBinding); 98 } 99 /// <summary> 100 /// 注冊命令道指定的命令控件。 101 /// </summary> 102 /// <param name="stripItem">綁定目標。</param> 103 /// <param name="command">綁定命令。</param> 104 /// <param name="staticSourceType">靜態類類型。</param> 105 /// <param name="propertyName">構造命令參數的名稱。</param> 106 /// <param name="booleanOppose">若值為System.Boolean值時,是否取反向值。</param> 107 /// <returns>返回一個命令目標實例。</returns> 108 public static CommandTarget Register(ToolStripItem stripItem, Command command, Type staticSourceType, string propertyName, bool booleanOppose = false) 109 { 110 var commandBinding = new CommandBinding(command, staticSourceType, propertyName, booleanOppose); 111 return Register(stripItem, commandBinding); 112 } 113 114 /// <summary> 115 /// 反注冊命令。 116 /// </summary> 117 /// <param name="target">注銷的命令目標。</param> 118 public static void Unregister(CommandTarget target) 119 { 120 if (target == null) 121 { 122 return; 123 } 124 if (targets.Contains(target)) 125 { 126 targets.Remove(target); 127 target.Dispose(); 128 } 129 } 130 }
最后附上委托命令(DegelateCommand)的實現。

1 /// <summary> 2 /// 表示一個可被執行委托的方法的命令。 3 /// </summary> 4 public sealed class DelegateCommand : Command 5 { 6 private Action<object> execute; 7 private Func<object, bool> canExecute; 8 /// <summary> 9 /// 初始化 <see cref="DelegateCommand"/> 新實例。 10 /// </summary> 11 /// <param name="execute">當命令被調用時,指定的方法。</param> 12 /// <param name="canExecute">當命令被確定是否能執行時,執行的方法。</param> 13 public DelegateCommand(Action<object> execute, Func<object, bool> canExecute = null) 14 { 15 this.execute = execute; 16 this.canExecute = canExecute; 17 } 18 /// <summary> 19 /// 定義用於確定此命令是否可以在其當前狀態下執行的方法。 20 /// </summary> 21 /// <param name="parameter">此命令使用的數據。 如果此命令不需要傳遞數據,則該對象可以設置為 null。</param> 22 /// <returns>如果可以執行此命令,則為 true;否則為 false。</returns> 23 public override bool CanExecute(object parameter) 24 { 25 if (canExecute == null) 26 { 27 return true; 28 } 29 return canExecute(parameter); 30 } 31 32 /// <summary> 33 /// 定義在調用此命令時調用的方法。 34 /// </summary> 35 /// <param name="parameter">此命令使用的數據。 如果此命令不需要傳遞數據,則該對象可以設置為 null。</param> 36 public override void Execute(object parameter) 37 { 38 if (CanExecute(parameter)) 39 { 40 execute(parameter); 41 } 42 } 43 }
補充:不好意思,忘記附上Demo示例,現補上,請前往 百度網盤 下載
本文如有紕漏,歡迎大家批評指正!