MenuStrip 動態產生 Item

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

2013-5-24 上午 11-22-54

寫完一般比較常見的方式後,突然有點想嘗試一些比較不一樣的做法,所以又做了一個比較不一樣的方式來產生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事件
    }
}


詳細內容可以下載原始檔範例,裡面也包含第一種方法的檔案

 

Technorati 的標籤: ,,