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

 

大叔~大婶~大哥~大嫂~大妹子~点个推荐吧~点个推荐吧~

posted @ 2011-12-08 00:15  liulun  阅读(7997)  评论(37编辑  收藏  举报