本文主要介紹WinForm項(xiàng)目中如何像WPF一樣優(yōu)雅的使用Command來(lái)實(shí)現(xiàn)業(yè)務(wù)操作。想必大家已經(jīng)疲于雙擊控件生成事件處理方法來(lái)實(shí)現(xiàn)業(yè)務(wù)操作,如多控件(按鈕、菜單、工具條、狀態(tài)欄等命令控件)來(lái)做同一個(gè)動(dòng)作,還要控制它們啟用(Enabled)狀態(tài)等等,使代碼結(jié)構(gòu)冗長(zhǎng)且可讀性差。下面具體介紹和實(shí)現(xiàn)WinForm中對(duì)Command的應(yīng)用。
顧 名思義,定義一個(gè)命令,繼承至System.Windows.Input.ICommand接口,實(shí)現(xiàn) Execute(object) ,CanExecute(object)方法和 CanExecuteChanged事件。由 CanExecute 確定是否調(diào)用 Execute 執(zhí)行該命令。
1 /// <summary> 2 /// 定義一個(gè)執(zhí)行的命令。 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 /// 當(dāng)出現(xiàn)影響是否應(yīng)執(zhí)行該命令的更改時(shí)發(fā)生。13 /// </summary>14 public event EventHandler CanExecuteChanged;15 16 /// <summary>17 /// 定義用于確定此命令是否可以在其當(dāng)前狀態(tài)下執(zhí)行的方法。18 /// </summary>19 /// <param name="parameter">此命令使用的數(shù)據(jù)。 如果此命令不需要傳遞數(shù)據(jù),則該對(duì)象可以設(shè)置為 null。</param>20 /// <returns>如果可以執(zhí)行此命令,則為 true;否則為 false。</returns>21 public abstract bool CanExecute(object parameter);22 23 /// <summary>24 /// 定義在調(diào)用此命令時(shí)調(diào)用的方法。25 /// </summary>26 /// <param name="parameter">此命令使用的數(shù)據(jù)。 如果此命令不需要傳遞數(shù)據(jù),則該對(duì)象可以設(shè)置為 null。</param>27 public abstract void Execute(object parameter);28 29 30 /// <summary>31 /// 提升命令狀態(tài)更改。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 /// 觸發(fā)當(dāng)命令狀態(tài)更改事件。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 }
提供給命令執(zhí)行時(shí)的參數(shù),該對(duì)象主要由參數(shù)源(Source)和參數(shù)源的屬性(Parameter)構(gòu)成。參數(shù)源可繼承至System.ComponentModels.INotifyPropertyChanged接口,實(shí)現(xiàn)屬性值更改通知。此 Parameter 必須為屬性(也可以是靜態(tài)屬性)。
1 /// <summary> 2 /// 表示命令參數(shù)。 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 /// 獲取一個(gè)值,該值表示命令參數(shù)源。 15 /// </summary> 16 /// <value>The source.</value> 17 public object Source 18 { 19 get { return source; } 20 } 21 22 /// <summary> 23 /// 獲取一個(gè)值,該值表示命令參數(shù)的類型若當(dāng)前非靜態(tài)類型綁定則為 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 /// 獲取一個(gè)值,該值表示命令執(zhí)行參數(shù)。 36 /// </summary> 37 /// <value>The parameter.</value> 38 public object Parameter { get { return ResolvePropertyValue(); } } 39 40 /// <summary> 41 /// 獲取一個(gè)值,該值表示參數(shù)所屬的命令。 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 新實(shí)例。 62 /// </summary> 63 /// <param name="source">綁定源。</param> 64 /// <param name="propertyMember">綁定成員(屬性名稱)。</param> 65 /// <param name="booleanOppose">若值為System.Boolean時(shí),是否取反。</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 新實(shí)例。 77 /// </summary> 78 /// <param name="source">綁定源。</param> 79 /// <param name="propertyMember">綁定成員(屬性名稱)。</param> 80 /// <param name="booleanOppose">若值為System.Boolean時(shí),是否取反。</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 /// 初始化一個(gè)可指定靜態(tài)成員的 RelateCommandParameter 新實(shí)例。 89 /// </summary> 90 /// <param name="staticSourceType">靜態(tài)類類型。</param> 91 /// <param name="propertyMember">綁定成員(屬性名稱)。</param> 92 /// <param name="booleanOppose">若值為System.Boolean時(shí),是否取反。</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 }
將命令和命令參數(shù)組合構(gòu)建成一個(gè)綁定對(duì)象。
1 /// <summary> 2 /// 定義命令綁定對(duì)象。 3 /// </summary> 4 public sealed class CommandBinding 5 { 6 /// <summary> 7 /// 獲取一個(gè)值,該值表示命令綁定的對(duì)象。 8 /// </summary> 9 /// <value>The command.</value>10 public Command Command { get; private set; }11 12 /// <summary>13 /// 獲取一個(gè)值,該值表示命令執(zhí)行參數(shù)。14 /// </summary>15 /// <value>The command parameter.</value>16 public CommandParameter CommandParameter { get; private set; }17 18 /// <summary>19 /// 初始化 <see cref="CommandBinding"/> 新實(shí)例。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"/> 新實(shí)例。30 /// </summary>31 /// <param name="command">要綁定的命令。</param>32 /// <param name="commandParameter">要綁定的命令參數(shù)。</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"/> 新實(shí)例。45 /// </summary>46 /// <param name="command">要綁定的命令。</param>47 /// <param name="source">要綁定的命令參數(shù)的實(shí)例。</param>48 /// <param name="propertyMember">要綁定的命令參數(shù)的屬性名稱。</param>49 /// <param name="booleanOppose">若值為System.Boolean值時(shí),是否取反向值。</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"/> 新實(shí)例。58 /// </summary>59 /// <param name="command">要綁定的命令。</param>60 /// <param name="staticSourceType">靜態(tài)類類型。</param>61 /// <param name="propertyMember">綁定成員(屬性名稱)。</param>62 /// <param name="booleanOppose">若值為System.Boolean時(shí),是否取反。</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 }
此為最終執(zhí)行命令的目標(biāo),構(gòu)建此對(duì)象實(shí)例時(shí)指定一個(gè)綁定(CommandBinding),監(jiān)視WinForm命令控件(如Control、ToolStripItem)等包含 Click 事件和 Enabled 屬性的對(duì)象。
1 /// <summary> 2 /// 表示命令綁定的執(zhí)行目標(biāo)。 3 /// </summary> 4 public sealed class CommandTarget : IDisposable 5 { 6 private readonly object target; 7 private bool disposed = false; 8 9 /// <summary> 10 /// 獲取一個(gè)值,該值表示目標(biāo)是否已釋放。 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 /// 獲取一個(gè)值,該值表示目標(biāo)的命令綁定源。 17 /// </summary> 18 /// <value>The command binding.</value> 19 public CommandBinding CommandBinding { get; private set; } 20 21 /// <summary> 22 /// 初始化 CommandTarget 新實(shí)例。 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 新實(shí)例。 34 /// </summary> 35 /// <param name="control">綁定目標(biāo)。</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 新實(shí)例。 48 /// </summary> 49 /// <param name="item">綁定目標(biāo)。</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 /// 執(zhí)行與釋放或重置非托管資源相關(guān)的應(yīng)用程序定義的任務(wù)。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控件)注冊(cè)與反注冊(cè)功能。
1 /// <summary> 2 /// 表示命令注冊(cè)管理器。 3 /// </summary> 4 public static class CommandBindingManager 5 { 6 private static readonly List<CommandTarget> targets = new List<CommandTarget>(); 7 8 /// <summary> 9 /// 注冊(cè)命令道指定的命令控件。 10 /// </summary> 11 /// <param name="control">綁定目標(biāo)。</param> 12 /// <param name="commandBinding">命令綁定源。</param> 13 /// <returns>返回一個(gè)命令目標(biāo)實(shí)例。</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 /// 注冊(cè)命令道指定的命令控件。 23 /// </summary> 24 /// <param name="stripItem">綁定目標(biāo)。</param> 25 /// <param name="commandBinding">命令綁定源。</param> 26 /// <returns>返回一個(gè)命令目標(biāo)實(shí)例。</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 /// 注冊(cè)命令道指定的命令控件。 36 /// </summary> 37 /// <param name="control">綁定目標(biāo)。</param> 38 /// <param name="command">綁定命令。</param> 39 /// <returns>返回一個(gè)命令目標(biāo)實(shí)例。</returns> 40 public static CommandTarget Register(Control control, Command command) 41 { 42 return Register(control, new CommandBinding(command)); 43 } 44 /// <summary> 45 /// 注冊(cè)命令道指定的命令控件。 46 /// </summary> 47 /// <param name="stripItem">綁定目標(biāo)。</param> 48 /// <param name="command">綁定命令。</param> 49 /// <returns>返回一個(gè)命令目標(biāo)實(shí)例。</returns> 50 public static CommandTarget Register(ToolStripItem stripItem, Command command) 51 { 52 return Register(stripItem, new CommandBinding(command)); 53 } 54 55 /// <summary> 56 /// 注冊(cè)命令道指定的命令控件。 57 /// </summary> 58 /// <param name="control">綁定目標(biāo)。</param> 59 /// <param name="command">綁定命令。</param> 60 /// <param name="source">構(gòu)造命令參數(shù)的源。</param> 61 /// <param name="propertyName">構(gòu)造命令參數(shù)的名稱。</param> 62 /// <param name="booleanOppose">若值為System.Boolean值時(shí),是否取反向值。</param> 63 /// <returns>返回一個(gè)命令目標(biāo)實(shí)例。</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 /// 注冊(cè)命令道指定的命令控件。 72 /// </summary> 73 /// <param name="stripItem">綁定目標(biāo)。</param> 74 /// <param name="command">綁定命令。</param> 75 /// <param name="source">構(gòu)造命令參數(shù)的源。</param> 76 /// <param name="propertyName">構(gòu)造命令參數(shù)的名稱。</param> 77 /// <param name="booleanOppose">若值為System.Boolean值時(shí),是否取反向值。</param> 78 /// <returns>返回一個(gè)命令目標(biāo)實(shí)例。</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 /// 注冊(cè)命令道指定的命令控件。 87 /// </summary> 88 /// <param name="control">綁定目標(biāo)。</param> 89 /// <param name="command">綁定命令。</param> 90 /// <param name="staticSourceType">靜態(tài)類類型。</param> 91 /// <param name="propertyName">構(gòu)造命令參數(shù)的名稱。</param> 92 /// <param name="booleanOppose">若值為System.Boolean值時(shí),是否取反向值。</param> 93 /// <returns>返回一個(gè)命令目標(biāo)實(shí)例。</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 /// 注冊(cè)命令道指定的命令控件。101 /// </summary>102 /// <param name="stripItem">綁定目標(biāo)。</param>103 /// <param name="command">綁定命令。</param>104 /// <param name="staticSourceType">靜態(tài)類類型。</param>105 /// <param name="propertyName">構(gòu)造命令參數(shù)的名稱。</param>106 /// <param name="booleanOppose">若值為System.Boolean值時(shí),是否取反向值。</param>107 /// <returns>返回一個(gè)命令目標(biāo)實(shí)例。</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 /// 反注冊(cè)命令。116 /// </summary>117 /// <param name="target">注銷(xiāo)的命令目標(biāo)。</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)的實(shí)現(xiàn)。
1 /// <summary> 2 /// 表示一個(gè)可被執(zhí)行委托的方法的命令。 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"/> 新實(shí)例。10 /// </summary>11 /// <param name="execute">當(dāng)命令被調(diào)用時(shí),指定的方法。</param>12 /// <param name="canExecute">當(dāng)命令被確定是否能執(zhí)行時(shí),執(zhí)行的方法。</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 /// 定義用于確定此命令是否可以在其當(dāng)前狀態(tài)下執(zhí)行的方法。20 /// </summary>21 /// <param name="parameter">此命令使用的數(shù)據(jù)。 如果此命令不需要傳遞數(shù)據(jù),則該對(duì)象可以設(shè)置為 null。</param>22 /// <returns>如果可以執(zhí)行此命令,則為 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 /// 定義在調(diào)用此命令時(shí)調(diào)用的方法。34 /// </summary>35 /// <param name="parameter">此命令使用的數(shù)據(jù)。 如果此命令不需要傳遞數(shù)據(jù),則該對(duì)象可以設(shè)置為 null。</param>36 public override void Execute(object parameter)37 {38 if (CanExecute(parameter))39 {40 execute(parameter);41 }42 }43 }
補(bǔ)充:不好意思,忘記附上Demo示例,現(xiàn)補(bǔ)上,請(qǐng)前往 百度網(wǎng)盤(pán) 下載
本文如有紕漏,歡迎大家批評(píng)指正!
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注