本文主要介绍WinForm项目中如何像WPF一样优雅的使用Command来实现业务操作。想必大家已经疲于双击控件生成事件处理方法来实现业务操作,如多控件(按钮、菜单、工具条、状态栏等命令控件)来做同一个动作,还要控制它们启用(Enabled)状态等等,使代码结构冗长且可读性差。下面具体介绍和实现WinForm中对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 }
提供给命令执行时的参数,该对象主要由参数源(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 else127 {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 else153 {154 throw new ArgumentNullException("propertyMember");155 }156 }157 }
将命令和命令参数组合构建成一个绑定对象。
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),监视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 }
命令绑定管理器提供命令绑定(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示例,现补上,请前往 百度网盘 下载
本文如有纰漏,欢迎大家批评指正!
新闻热点
疑难解答