命令者模式
最近整一个WinForm界面,上面拖了100来个控件-有菜单、工具栏、命令按钮,做的时候设置这些控件的Enabled就够头大了,在不画界面操作流程图的情况下做起来就是一团乱麻。通过坚韧不拔的意志做完后,突然想起很多年前MS的IssueVision中似乎有个叫命令者模式的东东,于是就翻出并整理如下:
命令者模式可以看作是命令模式的变形,主要用来解决UI上多个控件跟某个具体命令关联问题,一方面命令者将命令的Enabled状态变更转化成对应控件的Enabled状态变更,另一方面命令者提供对应控件点击事件的处理函数HandleUIEvent(将具体请求路由给内部引用Command处理)。
从图中可见每个命令者对应一个具体的控件并关联一个命令,命令者订阅命令的EnableChangedEventHandler事件(命令可用状态改变),通过事件模型(这个可以看作C#的观察者模式实现)设置对应的控件的Enabled属性,一个命令实例可以关联多个命令者实例(的如菜单命令者、button按钮命令者)
//==========命令的定义代码===============
典型的命令模式代码,只不过接收者(Receiver)是Action委托,应此就省略了ICommand接口

public class Command
{
// The EnableChanged event is raised when the IsEnabled value of the command
// changes. This is handled in the Commander objects to set the Enabled property
// of the managed controls.
public delegate void EnableChangedEventHandler(object sender, Command.EnableChangedEventArgs e);
public virtual event EnableChangedEventHandler EnableChanged;
public delegate void Action();
private Action m_action;
private bool m_isEnabled = true;
// The IsEnabled property is used to set/retrieve the enabled state of all UI
// controls that the command manages
public bool IsEnabled
{
get
{
return m_isEnabled;
}
set
{
if (m_isEnabled != value)
{
m_isEnabled = value;
if (EnableChanged != null)
{
EnableChanged(this, new EnableChangedEventArgs(IsEnabled));
}
}
}
}
public Command(Action action)
{
m_action = action;
}
// Invokes the method assigned to this command.
public void Execute()
{
m_action();
}
// Arguments passed to the EnableChanged event.
public class EnableChangedEventArgs : EventArgs
{
private bool m_isEnabled = false;
public bool IsEnabled
{
get
{
return m_isEnabled;
}
}
public EnableChangedEventArgs(bool isEnabled)
{
m_isEnabled = isEnabled;
}
}
}
//===================命令者实现==========================
使用了抽象类(应该是模板模式),来避免重复代码

public abstract class Commander
{
protected Command m_command;
protected abstract void HandleEnableChangedEvent(object sender, Command.EnableChangedEventArgs e);
protected Commander(Command command)
{
m_command = command;
m_command.EnableChanged += new Command.EnableChangedEventHandler(this.HandleEnableChangedEvent);
}
}
// MenuItemCommander class
public class MenuItemCommander : Commander
{
private MenuItem m_item;
protected MenuItemCommander(MenuItem item, Command command) : base(command)
{
m_item = item;
m_item.Click += new EventHandler(this.HandleUIEvent);
}
protected override void HandleEnableChangedEvent(object sender, Command.EnableChangedEventArgs e)
{
m_item.Enabled = e.IsEnabled;
}
private void HandleUIEvent(object sender, EventArgs e)
{
m_command.Execute();
}
// Connect is a shared (static) method that performs the task of adapting a menu
// item to a command. The commander exists only to wire up the two objects --
// it is not used further
public static void Connect(MenuItem item, Command command)
{
MenuItemCommander unused = new MenuItemCommander(item, command);
}
}
注意上面的Connect方法,期unused看上去是一个方法变量,但是在MenuItemCommander的构造函数中将该实例的HandleUIEvent方法绑定到菜单项的Click事件上了,m_item.Click += new EventHandler(this.HandleUIEvent);因此unused对象不会被释放
//===========MainForm(UI)===================

private void InitializeCommands()
{
// Wire up menus and toolbar buttons
//Command pattern: File | Send & Receive
//一个命令对应两个命令者(两控件)
m_sendReceiveCommand = new Command(new Command.Action(this.SendReceive_Action));
MenuItemCommander.Connect(menuSendReceive, m_sendReceiveCommand);
ToolBarButtonCommander.Connect(tlbSendReceive, m_sendReceiveCommand);
// Toolbar | Work Offline
m_workOfflineCommand = new Command(new Command.Action(this.WorkOffline_Action));
ToolBarButtonCommander.Connect(tlbOffline, m_workOfflineCommand);
// File | new
m_newIssueCommand = new Command(new Command.Action(this.NewIssue_Action));
MenuItemCommander.Connect(this.menuNew, m_newIssueCommand);
ToolBarButtonCommander.Connect(this.tlbNewIssue, m_newIssueCommand);
// File | Logout and Exit
m_logoutExitCommand = new Command(new Command.Action(this.LogoutExit_Action));
MenuItemCommander.Connect(this.mnuLogoutExit, m_logoutExitCommand);
// File | Exit
m_exitCommand = new Command(new Command.Action(this.Exit_Action));
MenuItemCommander.Connect(this.menuExit, m_exitCommand);
// Help | About
m_aboutCommand = new Command(new Command.Action(this.About_Action));
MenuItemCommander.Connect(this.menuAbout, m_aboutCommand);
}
命令接收者定义

#region Command Actions
private void SendReceive_Action()
{
this.Cursor = Cursors.WaitCursor;
this.m_issueSubject.SendReceiveIssueData(SyncThread.MainThread);
this.Cursor = Cursors.Default;
}
private void WorkOffline_Action()
{
UpdateOnlineStatus();
}
private void About_Action()
{
// Display the About dialog
AboutForm form = new AboutForm();
form.Visible = false;
form.ShowDialog(this);
}
private void NewIssue_Action()
{
// Display the new Issue dialog
IssueNewForm form = new IssueNewForm(this);
form.Subject = m_issueSubject;
form.Show();
}
private void LogoutExit_Action()
{
// Delete settings file and exit
UserSettings.Delete();
m_saveUserSettings = false;
this.Close();
}
private void Exit_Action()
{
this.Close();
}
#endregion
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
2009-04-07 Asp中的MSSQL数据库访问:driver={SQL Server};与Provider = Sqloledb;的区别