本文主要介绍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 } Command 代码
命令参数(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 } CommandParameter 代码
命令绑定(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 } CommandBinding 代码
命令目标(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 } CommandTarget 代码
命令绑定管理器(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 } CommandBindingManager 代码
最后附上委托命令(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 } DelegateCommand 代码
本文如有纰漏,欢迎大家批评指正!