現在,讓我們添加另一個 新的特性來查找命令句柄。一個類型應該可以很簡單的實現好幾個命令句柄,所 以你可以定義新的特性,讓插件的作者可以把它添加到命令句柄上。這個特性會 包含一參數,這些參數用於定義新的菜單命令應該放在什麼地方。每一個事件句 柄處理一個特殊的命令,而這個命令應該在菜單的某個特殊地方。為了標記一個 命令句柄,你要定義一個特性,用於標記一個屬性,讓它成為一個命令句柄,並 且申明菜單上的文字以及父菜單文字。DynamicCommand特性要用兩個參數來構造 :菜單命令文字以及父菜單的文字。這個特性類還包含一個構造函數,這個構造 函數用於為菜單初始化兩個字符串。這些內容同樣可以使用可讀可寫的屬性:
[AttributeUsage( AttributeTargets.Property ) ]
public class DynamicMenuAttribute : System.Attribute
{
private string _menuText;
private string _parentText;
public DynamicMenuAttribute( string CommandText,
string ParentText )
{
_menuText = CommandText;
_parentText = ParentText;
}
public string MenuText
{
get { return _menuText; }
set { _menuText = value; }
}
public string ParentText
{
get { return _parentText; }
set { _parentText = value; }
}
}
這個特性類已經做了標記,這樣它只能被應用到屬 性上。而命令句柄必須在類中以屬性暴露出來,用於提供給命令句柄來訪問。使 用這一技術,可以讓程序在啟動的時候查找和添加命令句柄的代碼變得很簡單。
現在你創建了這一類型的一個對象:查找命令句柄,以及添加它們到新 的菜單項中。你可以把特性和反射組合起來使用,用於查找和使用命令句柄屬性 ,對對象進行推測:
// Expanded from the first code sample:
// Find the types in the assembly
foreach( Type t in asm.GetExportedTypes( ) )
{
if (t.GetCustomAttributes(
typeof( CommandHandlerAttribute ), false).Length > 0 )
{
// Found a command handler type:
ConstructorInfo ci =
t.GetConstructor( new Type[0] );
if ( ci == null ) // No default ctor
continue;
object obj = ci.Invoke( null );
PropertyInfo [] pi = t.GetPropertIEs( );
// Find the propertIEs that are command
// handlers
foreach( PropertyInfo p in pi )
{
string menuTxt = "";
string parentTxt = "";
object [] attrs = p.GetCustomAttributes(
typeof ( DynamicMenuAttribute ), false );
foreach ( Attribute at in attrs )
{
DynamicMenuAttribute dym = at as
DynamicMenuAttribute;
if ( dym != null )
{
// This is a command handler.
menuTxt = dym.MenuText;
parentTxt = dym.ParentText;
MethodInfo mi = p.GetGetMethod();
EventHandler h = mi.Invoke( obj, null )
as EventHandler;
UpdateMenu( parentTxt, menuTxt, h );
}
}
}
}
}
private void UpdateMenu( string parentTxt, string txt,
EventHandler cmdHandler )
{
MenuItem menuItemDynamic = new MenuItem();
menuItemDynamic.Index = 0;
menuItemDynamic.Text = txt;
menuItemDynamic.Click += cmdHandler;
//Find the parent menu item.
foreach ( MenuItem parent in mainMenu.MenuItems )
{
if ( parent.Text == parentTxt )
{
parent.MenuItems.Add( menuItemDynamic );
return;
}
}
// Existing parent not found:
MenuItem newDropDown = new MenuItem();
newDropDown.Text = parentTxt;
mainMenu.MenuItems.Add( newDropDown );
newDropDown.MenuItems.Add( menuItemDynamic );
}