在前面介紹的幾篇關於CRM系統的開發隨筆中,裏面都整合了多個頁面的功能,包括多文檔界面,以及客戶相關信息的頁面展現,這個模塊就是利用DevExpress控件的XtraTabPage控件的動態加載實現的,本篇文章主要介紹兩種方式的動態加載,一個是對用戶控件(UserControl)模塊的動態加載,一個是對普通窗體(Form)的動態加載,經過這兩種方式,咱們有時候能夠動態實現很豐富的界面效果。html
參考了不少CRM的系統,通常都是把CRM系統中客戶相關的信息放到下邊,這也是成爲了一種通用的界面佈局,因爲客戶相關的信息不少,咱們能夠經過多文檔方式放在下面的右下角的佈局面板裏面,因爲整個程序是一個MDI多文檔界面,不能夠在子窗體裏面實現另一個MDI多文檔的呈現,可是咱們能夠另闢蹊徑,把它們放在一個TabPage頁面裏面,這樣咱們經過動態加載,也能實現咱們須要的界面佈局了。node
因爲咱們想動態加載客戶相關信息模塊,所以每一個模塊能夠經過用戶控件的方式獨立建立和維護,咱們在項目工程裏面建立相關的用戶控件,以下所示。數據庫
咱們爲了方便更好的控制,咱們須要定義一個全部用戶控件的基類(BasePageControl)方便處理,該類定義了一些常規的數據和接口,代碼以下所示。框架
public partial class BasePageControl : XtraUserControl, IPageApply { /// <summary> /// 控件的客戶ID /// </summary> public string CustomerID { get; set; } /// <summary> /// 登錄用戶基礎信息 /// </summary> public LoginUserInfo LoginUserInfo { get; set; }
...............
/// <summary> /// 用做頁面的應用接口 /// </summary> public interface IPageApply { /// <summary> /// 初始化頁面的相關用戶權限信息 /// </summary> /// <param name="userInfo">用戶信息</param> /// <param name="functionDict">權限信息</param> void InitFunction(LoginUserInfo userInfo, Dictionary<string, string> functionDict); /// <summary> /// 根據客戶ID屬性綁定數據 /// </summary> /// <param name="customerID">客戶ID</param> void BindData(string customerID); }
設計好基類,而後設計相關模塊的界面,繼承自BasePageControl便可,該模塊利用了個人Winform分頁控件實現數據的展現,並在頂端設計了一個工具欄Bar。函數
以上的界面和普通的列表界面差很少,只是沒有查詢條件而已,因此能夠利用代碼生成工具Database2Sharp生成一個標準的列表頁面,而後把大部分的代碼複製過來進行適當的調整便可。工具
public partial class ActivityControl : BasePageControl { public ActivityControl() { InitializeComponent(); InitDictItem(); this.winGridViewPager1.OnPageChanged += new EventHandler(winGridViewPager1_OnPageChanged); this.winGridViewPager1.OnStartExport += new EventHandler(winGridViewPager1_OnStartExport); this.winGridViewPager1.OnEditSelected += new EventHandler(winGridViewPager1_OnEditSelected); this.winGridViewPager1.OnAddNew += new EventHandler(winGridViewPager1_OnAddNew); this.winGridViewPager1.OnDeleteSelected += new EventHandler(winGridViewPager1_OnDeleteSelected); this.winGridViewPager1.OnRefresh += new EventHandler(winGridViewPager1_OnRefresh); this.winGridViewPager1.AppendedMenu = this.contextMenuStrip1; this.winGridViewPager1.ShowLineNumber = true; this.winGridViewPager1.BestFitColumnWith = false;//是否設置爲自動調整寬度,false爲不設置 this.winGridViewPager1.gridView1.DataSourceChanged += new EventHandler(gridView1_DataSourceChanged); this.winGridViewPager1.gridView1.CustomColumnDisplayText += new DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventHandler(gridView1_CustomColumnDisplayText); } .............................
設計好相關的客戶相關資料模塊後,咱們就須要在主界面(上圖)中加載相關的內容了,因爲前面博客隨筆說到,須要根據用戶的配置選擇進行動態加載,所以,咱們須要先獲取客戶的選擇列表,而後在根據列表進行判斷是否加載顯示。佈局
userPageList = BLLFactory<UserTreeSetting>.Instance.GetTreeSetting(pageCategory, LoginUserInfo.ID.ToString());
而後在獲取標準的Tab選項卡配置列表,代碼以下所示。post
List<SystemTreeNodeInfo> propList = BLLFactory<SystemTree>.Instance.GetTree(pageCategory);
接着定義一個函數,看標準的頁面列表是否包含在用戶配置列表裏面。this
/// <summary> /// 若是列表爲空或包含指定ID,則認爲包含 /// </summary> /// <param name="id">頁面ID節點</param> /// <returns></returns> private bool ContainPage(string id) { bool result = false; if (userPageList == null || userPageList.Count == 0 || userPageList.Contains(id)) { result = true; } return result; }
而後就是,根據相關信息,動態建立相關的Page頁面,而後添加到Tab選項卡控件裏面去便可,期間爲了方便對界面的控制顯示,咱們須要傳入主窗體的用戶身份信息。spa
XtraTabPage page = new XtraTabPage(); page.Name = nodeInfo.ID; page.Text = nodeInfo.TreeName; page.ImageIndex = i % 20;//設置圖標,總圖標只有21個 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);
建立對象的函數CreateCustomerControl函數實現以下所示,經過上面的代碼,咱們就能動態建立相關的頁面並顯示出來了。
/// <summary> /// 經過數據庫配置的控件名稱,反射建立對象 /// </summary> /// <param name="controlName">控件名稱(簡稱)</param> /// <returns></returns> private BasePageControl CreateCustomerControl(string controlName) { string namePrefix = "WHC.CRM.UI.CustomerPage."; controlName = namePrefix + controlName;//控件名稱全稱 BasePageControl userControl = null; try { userControl = CreateInstance(controlName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name); } catch (Exception ex) { LogTextHelper.Error(ex); } return userControl; } /// <summary> /// 根據全名和路徑構造對象 /// </summary> /// <param name="sName">對象全名</param> /// <param name="sFilePath">程序集路徑</param> /// <returns></returns> private static BasePageControl CreateInstance(string sName, string sFilePath) { Assembly assemblyObj = Assembly.Load(sFilePath); if (assemblyObj == null) { throw new ArgumentNullException("sFilePath", string.Format("沒法加載sFilePath={0} 的程序集", sFilePath)); } BasePageControl obj = (BasePageControl)assemblyObj.CreateInstance(sName); //反射建立 return obj; }
最後提一下,咱們建立的TabPage頁面,雖然能夠加上關閉圖標,可是默認是關閉不了頁面的,須要咱們特殊處理,也就是須要在後臺代碼移除選定的選項卡頁面,另外,爲了考慮若是客戶關閉了,咱們能夠保存它的設置,認爲它在配置中隱藏顯示這個頁面。具體的代碼邏輯以下所示。
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) { //Tree ID= Page.Name if(!string.IsNullOrEmpty(page.Name)); { nodeIdList.Add(page.Name); } } bool result = BLLFactory<UserTreeSetting>.Instance.SaveTreeSetting(pageCategory, LoginUserInfo.ID.ToString(), nodeIdList); }
第一小節咱們介紹了在TabControl控件裏面動態加載多個用戶控件(UserControl)頁面,可是,因爲個人CRM系統模塊會涉及不少相關數據處理的界面的,若是把界面所有封裝成用戶控件,使用起來可能會麻煩一些。
若有時候在啓動界面(Starter)模塊裏面,咱們可能在菜單配置一個獨立的模塊放到一級菜單裏面,若是是普通的列表窗口界面,那麼建立多文檔的子窗口就很是方便,若是是用戶控件,就調用不了;所以我但願可以儘量保留普通窗口的類型,而不是把它改成用戶控件類型。
在上圖中,咱們看到,每一個管理模塊裏面,還須要展現多個子模塊頁面,若是利用前面小節說到的用戶控件方式,在TabControl控件加載確定徹底沒有問題,本小節繼續探尋是否能夠不改變窗口類型的狀況下,動態加載普通窗口界面內容到TabControl控件裏面。
首先咱們設計一個模塊管理的主界面,包含了列表和TabControl的控件樣式。
而後再找後臺代碼實現TabControl控件動態加載普通窗口對象,建立窗口對象前,咱們須要判斷是否該類型已經在TabControl裏面存在了,若是存在者前置選項卡頁面便可,主要的加載代碼以下所示。
/// <summary> /// 加載或者激活指定類型的對話框 /// </summary> /// <param name="tabcontrol">XtraTabControl控件</param> /// <param name="formType">窗體類型,必須繼承自BaseForm類型</param> 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); }
以上就是在TabControl控件裏面,動態加載用戶控件、普通窗口的兩種不一樣的方式,都能爲咱們實現豐富的界面佈局展示,但願對你們在開發Winform界面效果上有所參考。
個人該CRM系統系列的幾篇隨筆連接以下,供閱讀。
Winform開發框架之客戶關係管理系統(CRM)的開發總結系列1-界面功能展現
Winform開發框架之客戶關係管理系統(CRM)的開發總結系列2-基於框架的開發過程