zoukankan      html  css  js  c++  java
  • WinForm企业应用框架设计【四】动态创建业务窗体

    一:自定义Tab按钮

    如图所示

    1

    我们的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

    image

    这个窗体基类有三个公开的属性

            /// <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安全验证

    http://www.cnblogs.com/liulun/archive/2011/12/08/2280110.html

  • 相关阅读:
    学习笔记1—多线程相关笔记
    【Java并发编程一】线程安全问题
    Intellij IDEA配置优化--转载
    idea使用阿里工具优化代码+阿里开发手册
    Navicat Premium 12.1.12.0安装与激活
    H5分享功能
    excel导出使用get请求参数过长问题
    百度地图Web引用
    idea通过mapper快速定位到xml文件
    Win10企业版永久激活方法(cmd命令)
  • 原文地址:https://www.cnblogs.com/ziyan22/p/2280612.html
Copyright © 2011-2022 走看看