Universal menu command handle pattern
summary
(This article is translate version , original version written in chinese , you can see it at http://www.cnblogs.com/xdesigner/archive/2006/10/07/522927.html )
Some WinForm application has a lot of menuitem , standard menum item in .NET framework is not perfect , so result in a lots of MenuItem_Click function , it is not easy to maintains those code , this article discuss a pattern which handle menu command universal .
I think some people complain that a part of the standard WinForm library in .NET Framework 1.1 is poor , some body fleer that Microsoft employ hight middle school students to write System.Windows.Forms , of cause , this viewpoint is inflation . but in the author's experience , I find that a part of the standard WinForm library is really poor .
Microsoft has thousands of good software engineer , according to it's power , it can provide a powerful , perfect standard WinForm library . Here , I guess microsoft publish a library with some disadvantage wilful . this is nothing but a business policy . Microsoft drop a blank and fetch the third party to exert . This can maintenance the microsoft's empire. I think it is a good idea .
Now talk about the content . In Winform application , you use a lot of menu , int VS.NET IDE , it provide a menu designer to design applicatin's menu structure , when you put a MainMenu or ContextMenu into a form , you can use menu designer to build menu item(type is System.Windows.Forms.MenuItem ) in a tree structure . and aim at every menuItem , you can write code to handle it's Click event .
This is fit to a small application , but not fit a big , complicated application with a lot of MenuItem , in a big applicatin , there are a lot of MenuItem_Click function , a WinForm application in a good design , it's logic collect together and call then use a single interface instead of distributed in lot's of MenuItem_Click or Button_Click functions . In this way , the MenuItem_Click function only call a single interface .
For example , a from define a golbal logic function interface , define as "void HandleCommand( string Command )" , in this function there are a big switch structure , this function execute logic action bases parameter "Command" . Then in this form , there are some code like following
void MenuItem_Open_Click( object Sender , System.EventArgs e )
{
HandleCommand( "Open" );
}
void MenuItem_Save_Click( object Sender , System.EventArgs e )
{
HandleCommand( "Save" );
}
There are a lot's of this kind of code is not a good smell , itt can be reform , you can let all MenuItem ' s Click event handler point to a same function , this function can write in
void MenuItem_Click( object Sender , System.EventArgs e )
{
if( Sender == MenuItem_Open )
HandleCommand( "Open" );
else if( Sender == MenuItem_Save )
HandleCommand( "Save" );
else if ..........
}
But this structure is not perfection , because people who write or maintain this function must know every MenuItem's name , this is do harm to maintain code , If a MenuItem's function changed , you must change this MenuItem's name and modify MenuItem_Click's code .
At there , How we hope than MenuItem type has Command property , if MenuItem has property Command , then the universal menum click handler can write like
void MenuItem_Click( object Sender , System.EventArgs e )
{
HandleCommand( ( ( MenuItem ) Sender ).Command );
}
Very simple , easy to maintains , all you do is manage every MenuItem's Command property .
But MenuItem has not Command property , so some people expend MenuItem type , build his owner MenuItem type , append Command property , then can use the universal MenuItem_Click function .
At there , I did not create my owner MenuItem type , stand two point , first , IDE's MenuItem designer does not support Custom MenuItem type . Secend , there are a lot of old application already use standard MenuItem type , Convert then to use Custom MenuItem type , it is a gread work.
So I bring out a universal MenuItem command handler model , the primary code no more than 100 , it can support single handle menu command event without custom MenuItem type , this code is following
public delegate void MenuCommandHandler( System.Windows.Forms.MenuItem MenuItem , string Command );
public class MenuCommandSender
{
public MenuCommandSender()
{
myHandler = new EventHandler( this.MenuClick );
}
public event MenuCommandHandler MenuCommand = null;
public void Clear()
{
foreach( BindItem item in myItems )
{
if( item.MenuItem != null )
{
item.MenuItem.Click -= myHandler ;
}
}
myItems.Clear();
}
public int Count
{
get{ return myItems.Count ;}
}
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 );
}
public string GetCommand( System.Windows.Forms.MenuItem MenuItem )
{
foreach( BindItem item in myItems )
{
if( item.MenuItem == MenuItem )
return item.Command ;
}
return null;
}
public System.Windows.Forms.MenuItem GetMenuItem( string strCommand )
{
foreach( BindItem item in myItems )
{
if( item.Command == strCommand )
return item.MenuItem ;
}
return null;
}
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;
}
}
}
}
}//public class MenuCommandSender
In your application , you create a MenuCommandSender instance , define a function as following and bind it to MenuCommandSender's MenuCommand event .
void HandleMenuCommand( MenuItem item , string Command )
{
HandleCommand( Command );
}
then use MenuCommandSender's Registe member function to registe menu command , for example
MenuCommandSender cmd = new MenuCommandSender();
cmd.MenuCommand += new MenuCommandHandler( HandleMenuCommand );
cmd.Registe( "Open" , MenuItem_Open );
cmd.Registe( "Save" , MenuItem_Save );
then , this pattern start . The work for convert old application use this model is acceptable .
In fact , this pattern can expend to handle System.Windows.Forms.Button or other WinForm control , not only System.Windows.Forms.MenuItem .
This pattern is very simple and useful , I hope it is benefic to some developer who handle a lot of MenuItems .
XDesigner Studio ( http://www.xdesigner.cn/default-eng.htm ) 2006-10-9