相信有不少人抱怨DOT.NET1.1中的WinForm库的某些缺陷,曾有人讥笑System.Windows.Forms下面的东西是微软请高中生写的,当然这话有些夸张了。但经过笔者的自己的实践,觉得标准WinForm库有些地方确实比较弱。
微软有成千的优秀的软件工程师,以它的实力应当会向大家贡献出一个强大完美的标准WinForm库的。但此处我猜测是微软应当是故意放出这个有点缺陷的标准库,无它,只是一种商业策略。微软故意留一手只是方便基于微软平台的第三方软件厂商的发挥。如果微软把什么都做了,第三方软件厂商干什么去啊。
微软阵营为什么如此强大,除了其核心微软公司强大外,还有大量的基于微软平台的第三方软件厂商包围着它,这造成了微软阵营的人多势众。这有点类似航母特混舰队,单个的航母虽然很强大,但也有对手,但若加上几艘吨位小一点的各种舰艇组成特混舰队那就几乎是无敌的了。从这点上看,盖茨真是非常聪明。
不韶了,现在说正文。在WinForm程序中是经常要使用菜单的,在VS.NET中使用菜单很方便,在窗体设计器中放入一个MainMenu或ContextMenu组件,然后使用菜单设计器往它添加树状结构的菜单项目(MenuItem)。针对每一个菜单项目处理它的Click事件。
这种方式对于小程序没问题,很方便,但对于菜单项目很多的大程序则比较累了,此时窗体代码中出现了大批的MenuItem_Click事件处理,设计良好的WinForm程序,它的处理逻辑应当集中起来实现而不应当分散在一个个MenuItem_Click或Button_Click,此时,菜单事件处理就只包含了简单的功能函数的调用了。
比如一个窗体定义了一个通用的功能函数的接口,HandleCommand( string Command ),这个函数内部就是一个大的Switch结构,根据Command内容进行相应的功能调用。此时窗体代码中可能出现类似代码了
void MenuItem_Open_Click( object Sender , System.EventArgs e )
{
HandleCommand( "Open" );
}
void MenuItem_Save_Click( object Sender , System.EventArgs e )
{
HandleCommand( "Save" );
}
大量出现这种简单的代码是不完善的,应当改正,可以让所有的菜单项目的Click事件指向一个函数,然后这个函数可以写成
void MenuItem_Click( object Sender , System.EventArgs e )
{
if( Sender == MenuItem_Open )
HandleCommand( "Open" );
else if( Sender == MenuItem_Save )
HandleCommand( "Save" );
else if ..........
}
这种代码结构还是不完善的。因为代码编写者和维护者需要知道定义各个MenuItem的名称,这不利于维护,若以后某个菜单项目功能改变了,则还需要修改定义菜单项目的名称和通用菜单事件处理函数。
这时我们是多么希望MenuItem对象能有Command的属性啊,若MenuItem有Command属性,则通用菜单事件处理函数就可以写成
void MenuItem_Click( object Sender , System.EventArgs e )
{
HandleCommand( ( ( MenuItem ) Sender ).Command );
}
这种结构代码简单,维护容易,只要维护好MenuItem的Command属性就可以了。
但是MenuItem并没有类似功能的属性,于是有人开始扩展了,从MenuItem上派生一个自己的MenuItem类型,添加上了Command属性,然后就可以实现上述功能了。
但此处我不想自己派生MenuItem类型,主要有两点原因,首先是派生的MenuItem类型无法应用到IDE的菜单设计器。其次是旧程序已经大量使用了标准的MenuItem类型,若要使用新MenuItem类型,则移植工作量不小。
在此我提出一个通用的菜单命令处理模式,代码不过百行,可以不扩展MenuItem而能实现单一的菜单事件处理模式,而且能方便的应用到旧程序中。这个处理模式的代码如下
/// 菜单命令处理委托
/// </summary>
public delegate void MenuCommandHandler( System.Windows.Forms.MenuItem MenuItem , string Command );
/// <summary>
/// 菜单命令发送者
/// </summary>
public class MenuCommandSender
{
/// <summary>
/// 初始化对象
/// </summary>
public MenuCommandSender()
{
myHandler = new EventHandler( this.MenuClick );
}
/// <summary>
/// 菜单命令事件
/// </summary>
public event MenuCommandHandler MenuCommand = null;
/// <summary>
/// 清空对象
/// </summary>
public void Clear()
{
foreach( BindItem item in myItems )
{
if( item.MenuItem != null )
{
item.MenuItem.Click -= myHandler ;
}
}
myItems.Clear();
}
/// <summary>
/// 已经绑定的菜单个数
/// </summary>
public int Count
{
get{ return myItems.Count ;}
}
/// <summary>
/// 注册菜单命令
/// </summary>
/// <param name="strCommand">菜单命令字符串</param>
/// <param name="MenuItem">菜单对象</param>
public void Registe( string strCommand , System.Windows.Forms.MenuItem MenuItem )
{
BindItem item = new BindItem();
item.Command = strCommand ;
item.MenuItem = MenuItem ;
MenuItem.Click += myHandler ;
myItems.Add( item );
}
/// <summary>
/// 获得指定菜单项目的命令字符串
/// </summary>
/// <param name="MenuItem">菜单项目</param>
/// <returns>菜单命令字符串,若未找到则返回空引用</returns>
public string GetCommand( System.Windows.Forms.MenuItem MenuItem )
{
foreach( BindItem item in myItems )
{
if( item.MenuItem == MenuItem )
return item.Command ;
}
return null;
}
/// <summary>
/// 获得指定命令的菜单对象
/// </summary>
/// <param name="strCommand">命令字符串</param>
/// <returns>菜单对象,若未找到则返回空引用</returns>
public System.Windows.Forms.MenuItem GetMenuItem( string strCommand )
{
foreach( BindItem item in myItems )
{
if( item.Command == strCommand )
return item.MenuItem ;
}
return null;
}
#region 内部代码群 ************************************************************************
private System.Collections.ArrayList myItems = new System.Collections.ArrayList();
private class BindItem
{
public System.Windows.Forms.MenuItem MenuItem = null;
public string Command = null;
}
private System.EventHandler myHandler = null;
private void MenuClick( object sender , System.EventArgs e )
{
if( MenuCommand != null )
{
foreach( BindItem item in myItems )
{
if( item.MenuItem == sender )
{
MenuCommand( item.MenuItem , item.Command );
break;
}
}
}
}
#endregion
}//public class MenuCommandSender
实际应用中,实例化一个 MenuCommandSender 对象,然后定义一个如下函数绑定到它的 MenuCommand 的事件上,
void HandleMenuCommand( MenuItem item , string Command )
{
HandleCommand( Command );
}
然后调用MenuCommandSender的Registe函数注册菜单命令,例如
MenuCommandSender cmd = new MenuCommandSender();
cmd.MenuCommand += new MenuCommandHandler( HandleMenuCommand );
cmd.Registe( "Open" , MenuItem_Open );
cmd.Registe( "Save" , MenuItem_Save );
如此以来,这个模式就可运行了。而且旧程序移植到这个模式所需的工作量是可以接受的。
其实这种模式稍加扩展就可以应用到 Sytem.Windows.Forms.Button 或其他WinForm控件。
这种菜单命令集中发送的模式原理简单,但却有用。希望对一些处理大量菜单项目的程序员们有所帮助。
XDesigner软件工作室( http://www.xdesigner.cn ) 2006-10-7