WinForm企业应用框架设计【四】动态创建业务窗体

简介: 闲话休提~ 一:自定义Tab按钮 如图所示 我们的tab按钮左部是文字;右部是关闭按钮; 此按钮有两种状态:选中和未选中 未选中的按钮鼠标滑上背景色会变为淡蓝色; 选中的按钮背景色是黄色 关闭按钮鼠标滑上去是深黄色   控件中涉及的属性和公开的事件属性 /// ...

闲话休提~

一:自定义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安全验证

 

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

目录
相关文章
|
2月前
|
API C# Windows
【C#】在winform中如何实现嵌入第三方软件窗体
【C#】在winform中如何实现嵌入第三方软件窗体
114 0
|
4月前
|
前端开发 C# 开发者
WPF开发者必读:MVVM模式实战,轻松构建可维护的应用程序,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离关注点,提高了代码的可维护性和可扩展性。本文详细介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定与逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种模式,开发者可以更高效地构建桌面应用程序。希望本文能帮助你在WPF开发中更好地应用MVVM模式。
205 1
|
4月前
|
容器 C# 开发者
XAML语言大揭秘:WPF标记的魅力所在,让你轻松实现界面与逻辑分离,告别复杂代码!
【8月更文挑战第31天】XAML提供了一种直观且易于维护的界面设计方式,使得开发者可以专注于逻辑和业务代码的编写,而无需关心界面细节。通过数据绑定、布局管理和动画效果等特性,XAML可以实现丰富的界面交互和视觉效果。在实际开发过程中,开发者应根据具体需求选择合适的技术方案,以确保应用程序能够满足用户的需求。希望本文的内容能够帮助您在WPF应用程序开发中更好地利用XAML语言。
48 0
|
编译器 C# Windows
C# 编写 WinForm 窗体应用程序(第一期)
WinForm 是 Windows Form 的简称,是基于 .NET Framework 平台的客户端(PC软件)开发技术,一般使用 C# 编程。
C# 编写 WinForm 窗体应用程序(第一期)
|
C# 数据安全/隐私保护 Windows
C#编写WinForm 窗体应用程序(第二期)
消息框在 Windows 操作系统经常用到,例如在将某个文件或文件夹移动到回收站中时系统会自动弹出如下图所示的消息框。
C#编写WinForm 窗体应用程序(第二期)
C#编写WinForm窗体应用程序(第四期)
在 C# 语言中 RadioButton 是单选按钮控件,多个 RadioButton 控件可以为一组,这一组内的 RadioButton 控件只能有一个被选中。
C#编写WinForm窗体应用程序(第四期)
C#编写WinForm窗体应用程序(第五期)
列表框 (ListBox) 将所提供的内容以列表的形式显示出来,并可以选择其中的一项或多项内容,从形式上比使用复选框更好一些。
C#编写WinForm窗体应用程序(第五期)
|
C# 数据安全/隐私保护
C# 编写 WinForm 窗体应用程序(第三期)
文本框 (TextBox) 是在窗体中输入信息时最常用的控件,通过设置文本框属性可以实现多行文本框、密码框等。
C# 编写 WinForm 窗体应用程序(第三期)
|
存储 算法 数据可视化
应用C#设计winform的一些心得
近期,因工作需要,应用C#设计了一个winform界面,主要是用来实现人员的量化积分管理,类似于很多单位的绩效考核管理系统那种。坦言之,这其实只是个人第二次涉猎winform窗体应用的设计(上一次还要追溯6-7年前的院校时期),上手还是比较慢的,前后大概花了10天时间。因为最后功能上还算比较满意,特写此文以作总结,记录当下。
611 0
应用C#设计winform的一些心得
|
开发框架 Oracle 关系型数据库
C/S架构Winform插件化框架,Winform通用界面框架
插件化框架特点: 1. 开发框架以模块化形式在逻辑上解耦 2. 开发框架模块以动态链接库(DLL文件)形式独立部署。 3. 模块主界面(frmBaseModule)用来分割系统功能菜单与功能按钮,作为各模块的入口界面。 4. 插件化框架核心功能-动态加载模块技术。
2264 0