上篇:MDI窗體的工具欄合并(ToolStrip Merging)

這個(gè)模型的大概構成是這樣的:
1個(gè)MDI主窗體,1~n個(gè)MDI子窗體;
主窗體上一般會(huì )有1個(gè)主菜單欄,1~n個(gè)主工具欄;
子窗體設計時(shí)上可能有0~1個(gè)主菜單欄,0~1個(gè)主工具欄,運行時(shí)子窗體的菜單欄和工具欄都顯示在主窗體上,與主窗體的菜單欄和工具欄合并。(實(shí)際上也許會(huì )有多個(gè)工具欄,但先簡(jiǎn)化為一個(gè)工具欄的模型,實(shí)現了一個(gè)工具欄的合并后,要實(shí)現多個(gè)工具欄合并也并非難事)。
對于菜單欄的合并,無(wú)論是傳統的Windows API編程或者Window Forms,都已經(jīng)完美地解決了,不再贅述。
工具欄合并的過(guò)程最好在子窗體的激活和關(guān)閉處理處理,一個(gè)容易想到的方法是寫(xiě)在子窗體的事件代碼中,要么需在每一個(gè)子窗體的代碼文件寫(xiě)上類(lèi)似處理的代碼,要么讓所有子窗體繼承自一個(gè)寫(xiě)好相關(guān)事件處理代碼的窗體。前者顯然不可取,后者在單根繼承的時(shí)代,剝奪了子窗體從其它窗體繼承的可能,也不是一個(gè)好辦法。同時(shí)還有一個(gè)弊端是子窗體要訪(fǎng)問(wèn)主窗體的內容,耦合度太高。
最佳方案也自然而然地出來(lái)了,即在主窗體的MdiChildActivate事件中處理(不能望文生義以為僅僅是子窗體的激活會(huì )觸發(fā)此事件,事實(shí)上子窗體的關(guān)閉也會(huì )觸發(fā)此事件)。這個(gè)時(shí)候應該把主窗體從上一個(gè)子窗體合并來(lái)的工具欄內容清除掉(如果有的話(huà)),并將新激活的子窗體上的待合并的工具欄內容合并到主窗體的工具欄上(如果新激活的子窗體上無(wú)待合并的工具欄內容或者M(jìn)diChildActivate是關(guān)閉最后一個(gè)子窗體觸發(fā)的時(shí)候則無(wú)此步)。為簡(jiǎn)化代碼,假設子窗體的工具欄總是合并到主窗體工具欄的最后位置。
這時(shí)候,主窗體必須知道子窗體的工具欄的構成,完全解耦是不太可能的。一種辦法是將子窗體的工具欄控件的作用范圍設置為public, 我采取了另外一個(gè)辦法,就是定義了一個(gè)接口,讓需合并工具欄的子窗口實(shí)現這個(gè)接口即可。
接口代碼如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace Osmanthus.WinForms
{
interface IToolStripMergableForm
{

ToolStrip MergedToolStrip
{
get;
}
}
}
讓子窗體實(shí)現這個(gè)接口,所要寫(xiě)的代碼也簡(jiǎn)單,只有一句,即:
public ToolStrip MergedToolStrip
{
get { return this.toolStrip1; }
}
剩下的內容似乎理想當然地是寫(xiě)主窗體的MdiChildActivate事件處理代碼,不過(guò)我想增加重用性,就把它寫(xiě)成了一個(gè)組件,以后只要把該組件拖放在MDI主窗體,并設置此組件MainToolStrip屬性為主窗體的工具欄(其實(shí)也可以寫(xiě)成自動(dòng)獲取默認屬性值,即主窗體上的最后一個(gè)ToolStrip控件),不需對再對主窗體寫(xiě)一行代碼。這個(gè)部件的代碼內容如下(其中為實(shí)現組件對Form的存取參照了Chris Sells的大作,我不知道是否有其它的更好辦法):
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.ComponentModel.Design;

namespace Osmanthus.WinForms
{
class SdiToolStripMerger : Component, ISupportInitialize
{
private Form hostingForm;
private ToolStrip mainToolStrip;
private IToolStripMergableForm currentMdiChild = null;

private List<ToolStripItem> toolStripItemList = new List<ToolStripItem>();

public ToolStrip MainToolStrip
{
get { return mainToolStrip; }
set { mainToolStrip = value; }
}

[BrowsableAttribute(
false)]
public Form HostingForm
{
get
{
if ((hostingForm == null) && this.DesignMode)
{
IDesignerHost designer
= this.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (designer != null)
{
hostingForm
= designer.RootComponent as Form;
}
}
return hostingForm;
}
set
{
hostingForm
= value; //這里需要改進(jìn)一下, 應該一旦設置了host form,就不能再修改.
}
}

#region ISupportInitialize Members

public void BeginInit()
{
}

public void EndInit()
{
if ((!DesignMode) && (hostingForm != null))
{
hostingForm.MdiChildActivate
+= new EventHandler(UpdateToolStrip);
};
}
#endregion

void UpdateToolStrip(object sender, EventArgs e)
{
//清除從上個(gè)激活子窗體合并來(lái)的工具欄內容
if (currentMdiChild != null)
{
foreach (ToolStripItem toolItem in toolStripItemList)
{
currentMdiChild.MergedToolStrip.Items.Add(toolItem);
}
toolStripItemList.Clear();
}

IToolStripMergableForm form
= (IToolStripMergableForm)hostingForm.ActiveMdiChild;
//如當前激活的子窗體實(shí)現了 IToolStripMergableForm接口,則合并其工具欄內容
if (form != null)
{

currentMdiChild
= form;
foreach (ToolStripItem toolItem in currentMdiChild.MergedToolStrip.Items)
{
toolStripItemList.Add(toolItem);
}
foreach (ToolStripItem toolItem in toolStripItemList)
{
mainToolStrip.Items.Add(toolItem);
}
}
else
{
currentMdiChild
= null;
toolStripItemList.Clear();
}
}
}
}

2006/12/25 附記:今天早上發(fā)現了.net Framework 2.0新增加的
一個(gè)類(lèi)ToolStripManager,使用此類(lèi)可以簡(jiǎn)化上述代碼。