你的位置:首页 > ASP.net教程

[ASP.net教程]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       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   }

    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 代码

 

 

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