話說在 Web Form 中如果要動態產生 Menu 連結到網頁,其實實作還蠻容易的,只要把對應的連結處理好就可以,但在 Windows Form 中如果要動態產生 Menu 就比較複雜如果硬要寫的話,處理方法也有可是程式就會跟老太婆的裹腳布一樣又臭又長,因此在這邊與另一位同事Kyle討論後,有了以下的想法,來解決動態載入Menu的問題。
要動態建置Menu的方法,我們採用了Reflection中的Activator.CreateInstance,先處理了透過User Control的名稱來建立物件的方法,這樣的方法可以讓物件被動態的產生,程式碼如下:
這個方法是要在當 Menu 被點選的時候被觸發,才會建立 User Control,並且把這個User Control 塞到 Frame 的 User Control指定的容器中,接著來看Menu怎麼被產生出來。
Menu被產生出來的方式其實是根據我們所定義的XML檔(將來可修改為任何資料來源)來決定,XML格式如下:
這個XML中定義了上層的選單如基本建檔、選單管理,第二層的選單如:基本建檔>公司基本建檔 或 選單管理>選單設定 所以讀取XML檔後就可以產生出第一層選單與第二層選單,為了處理這個XML所以也寫了一個 MenuHelpers 來處理XML其實就是將XML轉成DataSet然後取出需要的值再繫結成Menu。
這裡面透過了一個UtilityUI去處理繫節Menu上,也透過實作MyRenderer來賦予MentStrip中Renderer的功能。也撰寫MenuDelegate讓Menu在被點選時能觸發UserControl的實體化的事件。
最後面建立了一個User Contorl是個Frame用來定義選單與操作畫面的相對配置,所以當 Windows From 被啟動時
記得在Form被初始化的時候就呼叫這個事件Form_Load,這樣在視窗載入時就會呈現。
後記:
其實這個部分還可以已有所改善,例如加入權限值,就可以決定那些清單需要載入那些不用。但是這純粹就是Menu對應的資料來源的處理。這樣的Menu設計模式可以讓程式要變更配置與清單內容時能夠更有彈性。
範例檔案:MenuDemo.zip
要動態建置Menu的方法,我們採用了Reflection中的Activator.CreateInstance,先處理了透過User Control的名稱來建立物件的方法,這樣的方法可以讓物件被動態的產生,程式碼如下:
/// <summary>
/// 動態載入usercontrol
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void ucMenu1_MenuEvent(object sender, EventArgs e)
{
string ctlName = (sender as MenuDemo.Controls.ucMenu).CtlName;
try
{
var ctl = (UserControl)Activator.CreateInstance(Type.GetType(string.Format("MenuDemo.Controls.uc{0}", ctlName)));
this.pRight.Controls.Clear();
this.pRight.Controls.Add(ctl);
}
catch (Exception ex) { }
}
這個方法是要在當 Menu 被點選的時候被觸發,才會建立 User Control,並且把這個User Control 塞到 Frame 的 User Control指定的容器中,接著來看Menu怎麼被產生出來。
Menu被產生出來的方式其實是根據我們所定義的XML檔(將來可修改為任何資料來源)來決定,XML格式如下:
<?xml version="1.0" standalone="yes"?>
<Menu>
<MenuItem>
<FoldNo>INV100</FoldNo>
<PrgNo>INV110</PrgNo>
<PrgName>公司基本資料建檔</PrgName>
</MenuItem>
<MenuItem>
<FoldNo>INV100</FoldNo>
<PrgNo>INV120</PrgNo>
<PrgName>發票基本資料建檔</PrgName>
</MenuItem>
<MenuItem>
<FoldNo>INV900</FoldNo>
<PrgNo>INV910</PrgNo>
<PrgName>選單設定</PrgName>
</MenuItem>
<MenuFold>
<FoldNo>INV100</FoldNo>
<FoldName>基本建檔</FoldName>
</MenuFold>
<MenuFold>
<FoldNo>INV900</FoldNo>
<FoldName>選單管理</FoldName>
</MenuFold>
</Menu>
這個XML中定義了上層的選單如基本建檔、選單管理,第二層的選單如:基本建檔>公司基本建檔 或 選單管理>選單設定 所以讀取XML檔後就可以產生出第一層選單與第二層選單,為了處理這個XML所以也寫了一個 MenuHelpers 來處理XML其實就是將XML轉成DataSet然後取出需要的值再繫結成Menu。
/// <summary>
/// Menu繫結
/// </summary>
private void MenuBind()
{
this.menuStrip1.Renderer = new MyRenderer();
List<MENUITEM> iList = MenuHelpers.GetMenuItem();
List<MENUFOLD> fList = MenuHelpers.GetMenuFold();
for (int j = 0; j < fList.Count; j++)
{
string FoldNo = fList[j].FOLDNO;
string FoldName = fList[j].FOLDNAME;
ToolStripMenuItem subItem = UtilityUI.AddContextMenu(FoldName,FoldNo, this.menuStrip1.Items,null);
List<MENUITEM> pList = (from data in iList where data.FOLDNO == FoldNo select data).ToList<MENUITEM>();
for (int i = 0; i < pList.Count; i++)
{
string pFoldNo = pList[i].FOLDNO;
string PrgName = pList[i].PRGNAME;
string PrgNo = pList[i].PRGNO;
UtilityUI.AddContextMenu(PrgName, PrgNo,subItem.DropDownItems, new EventHandler(btn_ClickEvent));
}
}
foreach (var item in this.menuStrip1.Items.Cast<ToolStripMenuItem>())
{
GetCheckMenuItemText(item);
}
}
這裡面透過了一個UtilityUI去處理繫節Menu上,也透過實作MyRenderer來賦予MentStrip中Renderer的功能。也撰寫MenuDelegate讓Menu在被點選時能觸發UserControl的實體化的事件。
public delegate void MenuDelegate(object sender, EventArgs e);
public event MenuDelegate MenuEvent;//委派事件
void btn_ClickEvent(object sender, EventArgs e)
{
this.CtlName = (sender as ToolStripMenuItem).Tag.ToString();
if (MenuEvent != null)
{
this.MenuEvent(this, null);
}
}
最後面建立了一個User Contorl是個Frame用來定義選單與操作畫面的相對配置,所以當 Windows From 被啟動時
void MainForm_Load(object sender, EventArgs e)
{
ucFrame ucFrame1 = new ucFrame();
ucFrame1.Dock = DockStyle.Fill;
this.Controls.Add(ucFrame1);
}
記得在Form被初始化的時候就呼叫這個事件Form_Load,這樣在視窗載入時就會呈現。
public MainForm()
{
InitializeComponent();
this.Load += new EventHandler(MainForm_Load);
}
後記:
其實這個部分還可以已有所改善,例如加入權限值,就可以決定那些清單需要載入那些不用。但是這純粹就是Menu對應的資料來源的處理。這樣的Menu設計模式可以讓程式要變更配置與清單內容時能夠更有彈性。
範例檔案:MenuDemo.zip
留言