C#:WinForm之Command

  本文主要介绍WinForm项目中如何像WPF一样优雅的使用Command来实现业务操作。想必大家已经疲于双击控件生成事件处理方法来实现业务操作,如多控件(按钮、菜单、工具条、状态栏等命令控件)来做同一个动作,还要控制它们启用(Enabled)状态等等,使代码结构冗长且可读性差。下面具体介绍和实现WinForm中对Command的应用。

  1. 命令(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 代码
  2. 命令参数(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 代码
  3. 命令绑定(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 代码
  4. 命令目标(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 代码
  5. 命令绑定管理器(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 代码

   补充:不好意思,忘记附上Demo示例,现补上,请前往 百度网盘 下载

 

  本文如有纰漏,欢迎大家批评指正!                                   

原文地址:https://www.cnblogs.com/hoze/p/5066525.html