MenuStrip 動態產生 Item
主要功能:
從DB or XML得取Item清單,產生Menu
Key Point:
動態註冊MenuItem_Click事件
//註冊Click事件
MenuItem.Click += new EventHandler(MenuClicked);
//要用反射取得MenuItem_Click 後要開啟的視窗
Assembly frmMain = Assembly.GetEntryAssembly();
Type MyType = frmMain.GetType(dr["frm_Path"].ToString());
實際操作範例:
Code:
private void MainForm_Load(object sender, EventArgs e)
{
this.menuStrip1.Items.Clear();
DataSet ds = new DataSet();
//從XML取得Menu清單
ds.ReadXml("menu.xml");
this.CreateMenu(this.menuStrip1, new ToolStripMenuItem(), ds.Tables["t_menu_info"]);
}
/// <summary>
/// 建立MenuItem
/// </summary>
/// <param name="menuStrip">MenuStrip名稱</param>
/// <param name="fatherItem">父Item</param>
/// <param name="dt">資料來源</param>
void CreateMenu(MenuStrip menuStrip, ToolStripMenuItem fatherItem, DataTable dt)
{
//取得父階層編號
string fatherID;
if (fatherItem.Text == "")
fatherID = "-1";
else
fatherID = ((DataRow)fatherItem.Tag)["frm_id"].ToString();
//取得目前階層節點數
DataRow[] dr = dt.Select("frm_father_id='" + fatherID + "'", "frm_order");
foreach (DataRow row in dr)
{
//給予屬性
ToolStripMenuItem MenuItem = new ToolStripMenuItem();
MenuItem.Text = row["frm_cname"].ToString();
MenuItem.Tag = row;
//判斷是否為父層
if (row["frm_father_id"].ToString() == "-1")
//是>加入menuStrip
menuStrip.Items.Add((ToolStripItem)MenuItem);
else
//否>加入父MenuItem
fatherItem.DropDownItems.Add((ToolStripItem)MenuItem);
//遞迴 判斷是否有子MenuItem
this.CreateMenu(menuStrip, MenuItem, dt);
//判斷是否有Click事件
if (row["frm_Path"].ToString() != "")
//註冊Click事件
MenuItem.Click += new EventHandler(MenuClicked);
}
}
#region 註冊Menu_Click
void MenuClicked(object sender, EventArgs e)
{
ToolStripMenuItem mi = sender as ToolStripMenuItem;
//如果有子項目就不產生click
if (mi.HasDropDownItems)
return;
DataRow dr = mi.Tag as DataRow;
//要用反射取得Form
Assembly frmMain = Assembly.GetEntryAssembly();
Type MyType = frmMain.GetType(dr["frm_Path"].ToString());
//實際建立Form
Form MDIChild = (Form)Activator.CreateInstance(MyType);
MDIChild.MdiParent = this;
MDIChild.Show();
}
#endregion
資料來源XML:
<?xml version="1.0" encoding="UTF-8"?>
<ctsets>
<t_menu_info frm_id="1" frm_cname="項目1" frm_father_id="-1" frm_order="1.0000" />
<t_menu_info frm_id="2" frm_cname="Form1" frm_Path="DynamicMenu.Win.Form1" frm_father_id="1" frm_remark="f1" frm_order="2.0000" />
<t_menu_info frm_id="3" frm_cname="Form2" frm_Path="DynamicMenu.Win.Form2" frm_father_id="1" frm_remark="f2" frm_order="3.0000" />
<t_menu_info frm_id="4" frm_cname="項目2" frm_father_id="-1" frm_order="10.0000" />
<t_menu_info frm_id="5" frm_cname="Form3" frm_Path="DynamicMenu.Win.Form3" frm_father_id="4" frm_remark="f3" frm_order="7.0000" />
<t_menu_info frm_id="6" frm_cname="Form4" frm_Path="DynamicMenu.Win.Form4" frm_father_id="4" frm_remark="f4" frm_order="8.0000" />
</ctsets>
進階延伸:
另一種反向的作法,在要開啟的子視窗做描述,優點是不需要維護一個Menu清單
做法是要開啟的子視窗都繼承一個共用的BaseForm
寫完一般比較常見的方式後,突然有點想嘗試一些比較不一樣的做法,所以又做了一個比較不一樣的方式來產生Menu
我們的想法是,大部分表單的方式都是使用XML或是SQL來新增表單,但我希望的是不需維護XML和SQL讓程式一產生出來就會自動加到表單的方式
利用一個BaseForm讓所有需要使用的Form全部都繼承那個BaseForm,然後再一開始動態產生表單時利用反射去找出所有繼承BaseForm的檔案然後加入到表單
一開始加入表單的時候還算順利,但很快就遇上了第一個問題,就是如何加入名稱,總不能用程式本身的名字
又不希望再新增表單時就要去建置物件來取得變數名稱,這樣會讓整體效能變差,後面看了網路上許多資料後決定使用Attribute的方式來做
在BaseForm裡面新增一個自用的Attribute屬性,讓所有的Form都能使用Attribute去改變名字,也成功地讓所有的表單可以更改自己需要的名稱
而如果沒有輸入也會使用預設的名稱,但畢竟也只是我們的一個嘗試,所以目前也只做到這邊,覺得雖然還是有許多缺點和問題需要解決,但也是一個有趣的想法,所以也就一起放上來了。
KeyPoint:
//取得目前組件
Assembly a = Assembly.GetExecutingAssembly();
//找尋組件內所有類別型態
foreach (Type t in a.GetTypes())
{
//如果父類別是繼承自BaseForm的話
if (t.IsSubclassOf(typeof(BaseForm)))
{
String name = "";
string path = "";
//使用Attribute方式取得程式名稱
if (t.GetCustomAttributes(typeof(DynamicMenu.Win.BaseForm.FormNameAttribute), false).Count() > 0)
name = t.GetCustomAttributes(typeof(DynamicMenu.Win.BaseForm.FormNameAttribute), false)[0].ToString();
else
name = t.Name.ToString();
//程式路徑
path = t.ToString();
//TODO 根據Item的Name & Path去New相對應的Form並註冊Click事件
}
}
詳細內容可以下載原始檔範例,裡面也包含第一種方法的檔案