• 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示例,现补上,请前往 百度网盘 下载

     

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

  • 相关阅读:
    C#
    C#
    C#
    创建一个ROS包
    创建一个工作空间
    ROS的文件系统
    单一职责原因
    策略模式
    UML类图
    简单工厂模式
  • 原文地址:https://www.cnblogs.com/hoze/p/5066525.html
Copyright © 2020-2023  润新知