技术开发 频道

WinForm应用框架:动态创建业务窗体

  【IT168技术】自定义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

WinForm应用框架设计:业务窗体的基类

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

        /// <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事件是怎么注册的哦~

  原文链接:http://www.cnblogs.com/liulun/archive/2011/12/08/2280110.html

  前一篇文章:WinForm企业应用框架设计:框架窗体设计和动态创建菜单;

0
相关文章