闲话休提~
一:自定义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安全验证