閑話休提~
一:自訂Tab按鈕
我們的tab按鈕左部是文字;右部是關閉按鈕;
此按鈕有兩種狀態:選中和未選中
未選中的按鈕滑鼠滑上背景色會變為淡藍色;
選中的按鈕背景色是黃色
關閉按鈕滑鼠滑上去是深黃色
控制項中涉及的屬性和公開的事件屬性
/// <summary> /// Tab標題 /// </summary> public string Caption; /// <summary> /// 是否選中 /// </summary> bool IsSelected = true; /// <summary> /// 文字的顏色 /// </summary> Color StrColor = Color.Black; /// <summary> /// 寬度 /// </summary> int StrWidth; /// <summary> /// 選中事件 /// </summary> [Browsable(true)] [EditorBrowsable(EditorBrowsableState.Always)] public event EventHandler OnSelect; /// <summary> /// 單擊關閉按鈕事件 /// </summary> [Browsable(true)] [EditorBrowsable(EditorBrowsableState.Always)] public event EventHandler OnClose;
注釋還是比較清楚的,就不多說了
接著看這個控制項自己的事件
/// <summary> /// 滑鼠移入事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TabBTN_MouseEnter(object sender, EventArgs e) { if (!IsSelected) { this.BackColor = ColorTranslator.FromHtml("#4D6082"); } } /// <summary> /// 滑鼠移出事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TabBTN_MouseLeave(object sender, EventArgs e) { if (!IsSelected) { this.BackColor = ColorTranslator.FromHtml("#293955"); } } /// <summary> /// 滑鼠移動事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TabBTN_MouseMove(object sender, MouseEventArgs e) { var flag = IsMouseOnClosePoint(); if (flag) { DrawControl(Color.Black, Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(232)))), ((int)(((byte)(166)))))); } else { DrawControl(StrColor, this.BackColor); } } /// <summary> /// 重繪事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TabBTN_Paint(object sender, PaintEventArgs e) { DrawControl(StrColor, this.BackColor); }
移入和移出事件都是要觸發移動事件的
移動事件要先判斷滑鼠所在的位置,是不是出於關閉按鈕位置;
然後再根據滑鼠的位置以不同的顏色繪製控制項
下面看繪製控制項和判斷滑鼠位置的相關方法
/// <summary> /// 重寫建立事件 /// </summary> protected override void OnCreateControl() { base.OnCreateControl(); var g = this.CreateGraphics(); StrWidth = (int)g.MeasureString(Caption, SystemFonts.DefaultFont).Width; g.Dispose(); this.Width = StrWidth + 24; } /// <summary> /// 繪製控制項 /// </summary> /// <param name="fore"></param> /// <param name="bg"></param> void DrawControl(Color fore, Color bg) { var g = this.CreateGraphics(); g.DrawString(Caption, SystemFonts.DefaultFont, new SolidBrush(StrColor), new PointF(3, 8)); var p = new Pen(fore, (float)1); g.FillRectangle(new SolidBrush(bg), new Rectangle(StrWidth + 6, 7, 13, 13)); g.TranslateTransform(StrWidth + 12, 13); g.RotateTransform(45); for (var i = 0; i < 4; i++) { g.RotateTransform(90); g.DrawLine(p, 0, 0, 6, 0); } g.ResetTransform(); p.Dispose(); g.Dispose(); } /// <summary> /// 滑鼠位置 /// </summary> /// <returns></returns> public bool IsMouseOnClosePoint() { var p = this.PointToClient(MousePosition); var crx = new Rectangle(StrWidth + 3, 3, 16, 16); return crx.Contains(p); }
我們在建立控制項的時候得到了文本的寬度
根據這個寬度來繪製控制項文本和關閉按鈕的位置
我們在屬性裡為這個控制項定義了事件的handler
下面看看這些handler是怎麼觸發的
/// <summary> /// 取消選中 /// </summary> public void DisSelectMe() { IsSelected = false; this.BackColor = ColorTranslator.FromHtml("#293955"); StrColor = Color.White; DrawControl(StrColor, this.BackColor); } /// <summary> /// 選擇中 /// </summary> public void SelectMe() { IsSelected = true; this.BackColor = SystemColors.Info; StrColor = Color.Black; DrawControl(StrColor, this.BackColor); } /// <summary> /// 觸發自訂事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TabBTN_Click(object sender, EventArgs e) { var flag = IsMouseOnClosePoint(); if (flag) { OnClose(this, EventArgs.Empty); } else { if (IsSelected) { return; } OnSelect(this, EventArgs.Empty); SelectMe(); } }
到此為止完成了tab按鈕的製作
可能有些地方還做的不是很完美~歡迎批評指正
二:業務表單的基類
所有的業務表單都繼承自這個基類BaseForm
這個表單基類有三個公開的屬性
/// <summary> /// 菜單資料 /// </summary> public MenuModel FormMenu { get; set; } /// <summary> /// tab按鈕 /// </summary> public TabBTN FormTabBTN { get; set; } /// <summary> /// 子功能表 /// </summary> public Label SubMenu { get; set; }
這三個屬性在後面會用到
這裡先不說了
/// <summary> /// 建構函式 /// </summary> public BaseForm() { InitializeComponent(); this.TopLevel = false; }
一般頂層表單是不允許被當作子控制項放在容器控制項中的
所以我們要設定表單的TopLevel屬性
/// <summary> /// tab按鈕選中事件; /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public virtual void tbn_OnSelect(object sender, EventArgs e) { this.Show(); } /// <summary> /// tab按鈕關閉事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public virtual void tbn_OnClose(object sender, EventArgs e) { this.Close(); }
這是tab按鈕的兩個事件~
在建立tab按鈕的時候註冊的~
待會我們再說怎麼建立的tab按鈕和註冊這兩個事件~
因為並不是在baseForm裡建立的tab按鈕
private void BaseForm_VisibleChanged(object sender, EventArgs e) { if (Utils.IsInDesignMode()) { return; } this.VisibleChanged -= new EventHandler(BaseForm_VisibleChanged); var mf = Utils.GetMainForm(); if (this.Visible) { foreach (var hf in mf.FormHistory) { if (hf.FormMenu.Url.Equals(this.FormMenu.Url)) { continue; } if (hf.Visible) { hf.Hide(); } } FormTabBTN.SelectMe(); mf.FormHistory.Remove(this); mf.FormHistory.Insert(0, this); mf.MainContainerP.Controls.Clear(); mf.MainContainerP.Controls.Add(this); SubMenu.BackColor = SystemColors.Info; //TODO:系統名稱可以做到資料庫裡去 mf.Text = string.Format("XXX管理系統-{0}", FormMenu.MenuName); } else { FormTabBTN.DisSelectMe(); SubMenu.BackColor = Color.Transparent; } this.VisibleChanged += new EventHandler(BaseForm_VisibleChanged); }
這是BaseForm的一個重要事件
隱藏和顯示切換的時候被觸發
如果從隱藏變為顯示
先遍曆所有開啟過的業務表單,如果有是顯示狀態的,那麼就把他隱藏掉,因為當前系統只能有一個業務表單是出於顯示狀態的
接著選中TAB按鈕,
FormHistory的Remove和Insert主要是為了讓系統記住哪些表單是最近顯示過的;
MainContainerP的Clear和Add是為了讓表單顯示在容器控制項內
如果從顯示變為隱藏
TAB按鈕取消選中,
子功能表的背景顏色變成透明的,(其實就是子功能表取消選中)
事件處理的開始取消了事件註冊
事件處理的結束有把事件註冊進去了
這樣做主要是為了避免多次觸發事件
Utils.GetMainForm();擷取主視窗的代碼如下:
/// <summary> /// 主視窗 /// </summary> private static MainForm mf; /// <summary> /// 擷取主視窗 /// </summary> /// <returns></returns> public static MainForm GetMainForm() { if (mf == null) { mf = Application.OpenForms["MainForm"] as MainForm; } return mf; }
當業務表單關閉時要處理一些邏輯
代碼如下
private void BaseForm_FormClosing(object sender, FormClosingEventArgs e) { this.VisibleChanged -= new EventHandler(BaseForm_VisibleChanged); var mf = Utils.GetMainForm(); mf.FormHistory.Remove(this); this.SubMenu.BackColor = Color.Transparent; if (mf.FormHistory.Count > 0) { mf.FormHistory[0].Show(); } foreach (var f in mf.FormHistory) { if (f.FormTabBTN.Left > FormTabBTN.Left) { f.FormTabBTN.Left -= (FormTabBTN.Width + 6); } } mf.TabContainerP.Controls.Remove(FormTabBTN); }
取消事件註冊
移除記錄
取消子功能表選中
開啟最近一次開啟的業務表單(如果有的話)
重寫設定tab按鈕的位置(主要是被關閉的tab按鈕的右邊的tab按鈕)
刪除tab按鈕
三:動態建立業務表單
我們在上一節中只講了子功能表的滑入和滑出事件,而沒有講單擊事件
單擊事件就是建立業務表單的事件了
來看一下代碼
/// <summary> /// 子功能表彈起事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void sm_MouseUp(object sender, MouseEventArgs e) { var lb = sender as Label; var m = lb.Tag as MenuModel; if (string.IsNullOrEmpty(m.Url)) { Utils.Alert("沒有與此菜單相關的業務表單"); return; } BaseForm f = null; foreach(var hf in FormHistory) { if (hf.FormMenu.Url.Equals(m.Url)) { f = hf; break; } } if (f == null) { f = CreateOneForm(m); f.SubMenu = lb; } if (f != null&&!f.Visible) { f.Show(); } }
其實這個方法裡的商務邏輯不多
主要的還是f = CreateOneForm(m);這一句
/// <summary> /// 建立一個業務表單;包括tab按鈕 /// </summary> /// <param name="m"></param> private BaseForm CreateOneForm(MenuModel m) { var ass = this.GetType().Assembly; var url = string.Format("XL.Client.Forms.{0}", m.Url); BaseForm f = null; try { f = ass.CreateInstance(url) as BaseForm; } catch { Utils.Alert("沒有與此菜單相關的業務表單"); return null; } f.Dock = DockStyle.Fill; f.FormMenu = m; var tabBtn = new TabBTN(); tabBtn.OnClose += new EventHandler(f.tbn_OnClose); tabBtn.OnSelect += new EventHandler(f.tbn_OnSelect); tabBtn.Caption = m.MenuName; int left = 6; var tabCount = TabContainerP.Controls.Count; if (tabCount > 0) { var lastTab = TabContainerP.Controls[tabCount - 1]; left += (lastTab.Left + lastTab.Width); } tabBtn.Left = left; TabContainerP.Controls.Add(tabBtn); f.FormTabBTN = tabBtn; return f; }
我們把菜單的URL欄位拿出來,反射了一個業務表單的執行個體
然後建立了tab按鈕的執行個體,並讓這個業務表單持有這個執行個體
注意tab按鈕的close和select事件是怎麼註冊的哦~ 親~
好吧~就這些~
今天的內容比較多~
寫的匆忙~有問題大家盡情的提吧~
接下來的內容是:登入、閃屏、用戶端快取資料、WCF安全驗證
大叔~大嬸~大哥~大嫂~大妹子~點個推薦吧~點個推薦吧~