最近整一个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