在MDI主界面下承載海量業務頁籤時,靜態設計顯得僵化且難以擴展。Winform開發框架CRM模塊採用 XtraTabControl 動態加載技術,支持「用戶控件」與「普通窗體」兩種載體模式,配合反射創建、權限上下文傳遞、頁籤關閉持久化,構建高靈活、強解耦的多文檔界面架構。

定義 BasePageControl : XtraUserControl, IPageApply,規範權限初始化、數據綁定標準:
public interface IPageApply {
void InitFunction(LoginUserInfo userInfo, Dictionary<string, string> functionDict);
void BindData(string customerID);
}
public partial class BasePageControl : XtraUserControl, IPageApply {
public string CustomerID { get; set; }
public LoginUserInfo LoginUserInfo { get; set; }
// ...
}
各功能模塊(如往來記錄、合同、服務等)繼承 BasePageControl,利用 WinGridViewPager 實現列表展示,工具欄集成新增/編輯/刪除/導出操作。

public partial class ActivityControl : BasePageControl {
public ActivityControl() {
InitializeComponent();
InitDictItem();
this.winGridViewPager1.OnPageChanged += new EventHandler(winGridViewPager1_OnPageChanged);
this.winGridViewPager1.OnEditSelected += new EventHandler(winGridViewPager1_OnEditSelected);
this.winGridViewPager1.OnAddNew += new EventHandler(winGridViewPager1_OnAddNew);
// ... 其他事件綁定
}
// 實現 IPageApply.BindData 等方法
}
根據用戶個性化配置(UserTreeSetting)與系統默認頁籤定義(SystemTree),反射創建控件實例並加入 XtraTabControl:
// 獲取用戶配置的頁籤ID列表
userPageList = BLLFactory<UserTreeSetting>.Instance.GetTreeSetting(pageCategory, LoginUserInfo.ID.ToString());
// 獲取系統默認頁籤定義
List<SystemTreeNodeInfo> propList = BLLFactory<SystemTree>.Instance.GetTree(pageCategory);
// 判斷是否顯示
private bool ContainPage(string id) {
return userPageList == null || userPageList.Count == 0 || userPageList.Contains(id);
}
// 動態創建並添加頁籤
foreach (var nodeInfo in propList) {
if (ContainPage(nodeInfo.ID)) {
XtraTabPage page = new XtraTabPage { Name = nodeInfo.ID, Text = nodeInfo.TreeName, ImageIndex = i % 20 };
BasePageControl control = CreateCustomerControl(nodeInfo.SpecialTag); // 反射創建
if (control != null) {
control.InitFunction(LoginUserInfo, FunctionDict); // 傳遞權限上下文
control.Dock = DockStyle.Fill;
page.Controls.Add(control);
}
this.tabCustomerRelated.TabPages.Add(page);
}
}
// 反射創建控件實例
private BasePageControl CreateCustomerControl(string controlName) {
string namePrefix = "WHC.CRM.UI.CustomerPage.";
controlName = namePrefix + controlName;
try {
return CreateInstance(controlName, Assembly.GetExecutingAssembly().GetName().Name);
} catch (Exception ex) { LogTextHelper.Error(ex); return null; }
}
private static BasePageControl CreateInstance(string sName, string sFilePath) {
Assembly assemblyObj = Assembly.Load(sFilePath);
return (BasePageControl)assemblyObj.CreateInstance(sName);
}
處理 CloseButtonClick 事件,移除頁籤並保存用戶最新配置:
private void tabCustomerRelated_CloseButtonClick(object sender, EventArgs e) {
this.tabCustomerRelated.TabPages.RemoveAt(this.tabCustomerRelated.SelectedTabPageIndex);
List<string> nodeIdList = new List<string>();
foreach (XtraTabPage page in this.tabCustomerRelated.TabPages) {
if (!string.IsNullOrEmpty(page.Name)) nodeIdList.Add(page.Name);
}
BLLFactory<UserTreeSetting>.Instance.SaveTreeSetting(pageCategory, LoginUserInfo.ID.ToString(), nodeIdList);
}

場景:一級菜單項對應獨立窗體(如產品管理、廠商管理),需在Tab中承載且保持窗體獨立性(可單獨彈出、複用邏輯)。
左側導航欄(NavBarControl),右側 XtraTabControl 承載內容頁。

private void LoadTabPageForm(XtraTabControl tabcontrol, Type formType, int imageIndex) {
bool found = false;
XtraTabPage selectedPage = null;
// 查找是否已打開
foreach (XtraTabPage page in tabcontrol.TabPages) {
if (page.Tag != null && page.Tag.ToString() == formType.Name) {
found = true; selectedPage = page; break;
}
}
if (!found) {
selectedPage = new XtraTabPage();
BaseDock dlg = (BaseDock)Activator.CreateInstance(formType); // 創建窗體實例
dlg.Visible = true;
dlg.Dock = DockStyle.Fill;
dlg.FormBorderStyle = FormBorderStyle.None;
dlg.TopLevel = false; // 關鍵:非頂級窗體,可嵌入容器
dlg.InitFunction(LoginUserInfo, FunctionDict); // 傳遞權限上下文
selectedPage.Text = dlg.Text;
selectedPage.ImageIndex = imageIndex;
selectedPage.Tag = dlg.GetType().Name;
selectedPage.Controls.Add(dlg);
tabcontrol.TabPages.Add(selectedPage);
}
selectedPage.BringToFront();
tabcontrol.SelectedTabPage = selectedPage;
}
private void itemProduct_LinkClicked(object sender, DevExpress.XtraNavBar.NavBarLinkEventArgs e) {
LoadTabPageForm(this.xtraTabMain, typeof(FrmProduct), 0);
}
private void itemManufacturer_LinkClicked(object sender, DevExpress.XtraNavBar.NavBarLinkEventArgs e) {
LoadTabPageForm(this.xtraTabMain, typeof(FrmManufacturer), 1);
}
private void itemCompetitor_LinkClicked(object sender, DevExpress.XtraNavBar.NavBarLinkEventArgs e) {
LoadTabPageForm(this.xtraTabMain, typeof(FrmCompetitor), 2);
}
private void xtraTabMain_CloseButtonClick(object sender, EventArgs e) {
XtraTabPage currentPage = this.xtraTabMain.SelectedTabPage;
BaseForm form = currentPage.Controls[0] as BaseForm;
if (form != null) { form.Close(); form.Dispose(); }
this.xtraTabMain.TabPages.RemoveAt(this.xtraTabMain.SelectedTabPageIndex);
}
| 維度 | UserControl模式 | Form模式 |
|---|---|---|
| 適用場景 | 從屬於主界面的功能分頁(如客戶詳情子模塊) | 獨立業務模塊,需支持單獨彈出/菜單直達 |
| 代碼復用 | 僅限Tab容器內 | 可同時作為MDI子窗體、獨立對話框、Tab頁使用 |
| 生命周期 | 隨頁籤銷毀 | 需手動 Dispose 釋放非託管資源 |
| 權限傳遞 | 統一通過 IPageApply.InitFunction | 統一通過 IFunction.InitFunction(Form實現接口) |
雙模式動態加載機制賦予框架極強的界面組織能力:UserControl模式構建聚合視圖,Form模式支撐模塊獨立演進。核心關鍵點在於 TopLevel=false 實現窗體容器化、統一接口傳遞權限上下文、反射解耦具體類型、關閉事件釋放資源。開發者可根據業務屬性靈活選擇,最大化代碼復用與界面靈活性。