WinForm企业应用框架设计【三】框架窗体设计;动态创建菜单;

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: 要不是我的朋友乔乔==乔不死跟我聊到领域驱动设计~ 我也不会发现第一篇中关于“充血实体”的错误说法(至少~我写文章的时候~内心的想法是错的~) 我个人不是很喜欢领域驱动设计~感觉这种思路(我们暂且叫它思路~虽然它有一些既有的原则和模式) 重点要求架构师深入到业务领域中去~ 但是在国内往往很难...

要不是我的朋友乔乔==乔不死跟我聊到领域驱动设计~

我也不会发现第一篇中关于“充血实体”的错误说法(至少~我写文章的时候~内心的想法是错的~)

我个人不是很喜欢领域驱动设计~感觉这种思路(我们暂且叫它思路~虽然它有一些既有的原则和模式)

重点要求架构师深入到业务领域中去~

但是在国内往往很难真正的与领域专家做深入交流~

架构师划分的领域模型和聚合往往与真实的情况差别较大~

即使划分的较好~新的业务和变化的业务也另设计师非常头疼~

另外

设计师很难将庞大复杂的业务抽象成领域模型

往往需要引入更为复杂的模型以对真实业务进行建模

-----------

xuefly说多放点内容出来~好吧~这次多一些(多了吗?)~

奔放的胸毛 等好几个朋友都对源码比较感兴趣~

我看你们都是娈童癖~这玩意还没发育成熟~就拿过去搞~有啥意思~

我下一章打算写“登录;闪屏;客户端数据缓存;WCF安全验证”

(这些东西的代码还没个影子)

----------------------

问题一:关于调试

如果你的跟着我的章节在做练习~

那么你可能会遇到从客户端单步调试进入到WCF端的过程

我的WCF是直接用的IIS7.5的虚拟目录

单步跳入WCF之前会提示

直接点[附加]就可以调试了~

问题二:关于创建动态WCF服务不完善的地方

在本系列第一篇中,我们使用了众多servicefactory来创建服务;这样是不好的

我对那段代码做了修改

请看这里:http://www.cnblogs.com/liulun/archive/2011/11/29/2268337.html

 

好吧!言归正传

一:框架窗体

先看图片 

框架窗体分管布局的只有四个Panel;

上、下、左、右。(搞过EXTJS的人比较喜欢说成东、南、西、北)-_-!

最上面的Panel是存放顶级菜单用的 (top menu)

最下面的panel是存放状态信息和系统版本用的

左边的Panel又分为两个panel

上面的是sub menu header 

下面的是sub menu

当点击一个top menu之后,sub menu中将出现所有此top menu下的子菜单

sub menu header就是这个top menu的名字

(因为我们的top menu没有选中状态;所以这里做一个sub menu header;让用户知道他点的是哪个顶菜单;sub menu就有选中状态了)

右侧的Panel也分为两个Panel

  上面的是tabs

  下面的是child form

   tabs是为了存放用户打开过的业务窗体的标题;当用户点击某个tab,将激活该窗体(在child form中显示)

  child form是当前正在操作的业务窗体

  (这里有例子会容易理解一些)

左右panel中间夹着一个splitter

此splitter可以拖动改变左右panel的宽度

这里需要注意一点

应该先把左侧panel拖进窗体,设置Dock left,

再拖一个splitter进窗体,他是天然的Dock left,

再拖右侧panel进窗体,设置Dock fill

这样splitter才会起作用

至于怎么把其他panel拖动到这个窗体中来~我就不多说了

二:动态创建顶部菜单

上一章中我们成功的访问WCF并得到了所有的MENU

现在我们就准备在界面上显示菜单 

        private void MainForm_Load(object sender, EventArgs e)
        {
            if (!Utils.IsInDesignMode())
            {
                InitMenu();
            }
        }

IsInDesignMode是为了判断当前的窗体是不是出于设计状态

(设计状态会执行一些代码~如果不进行处理~窗体就无法设计)

代码如下~

        /// <summary>
        /// 判断是否为设计状态
        /// </summary>
        /// <returns></returns>
        public static bool IsInDesignMode()
        {
            bool returnFlag = false;
            #if DEBUG
            if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
            {
                returnFlag = true;
            }
            else if (System.Diagnostics.Process.GetCurrentProcess().ProcessName.ToUpper().Equals("DEVENV"))
            {
                returnFlag = true;
            }
            #endif
            return returnFlag;
        }

由于创建菜单~和响应菜单的点击事件需要很多代码

我们把这些与菜单相关的代码统一放在一个partial类里

        /// <summary>
        /// 菜单缓存
        /// </summary>
        public List<MenuModel> Menus;
        /// <summary>
        /// 初始化菜单
        /// </summary>
        private void InitMenu()
        {
            PrepareMenus();
            CreateTopMenu();            
        }
        /// <summary>
        /// 从WCF获取所有菜单
        /// </summary>
        private void PrepareMenus()
        {
            var factory = new Common.ClientFactory<IMenu>();
            try
            {
                var client = factory.CreateClient();
                Menus = client.GetAllMenu();
            }
            catch (Exception ex)
            {
                Utils.OnException(ex);
            }
            factory.Dispose();
        }

如你所见~我已经对上一章中写的PrepareMenus做了一些修改~这些修改是为下一章服务~现在先不讲

先看CreateTopMenu

        /// <summary>
        /// 创建顶部菜单
        /// </summary>
        private void CreateTopMenu()
        {
            var tmms = (from v in Menus where v.ParentId == Guid.Empty orderby v.OrderNum select v).ToList();
            for (var i = 0; i < tmms.Count; i++)
            {
                var ctl = CreateOneTopMenu(tmms[i], i);
                TopMenuP.Controls.Add(ctl);
            }
        }
        /// <summary>
        /// 创建一个顶部菜单
        /// </summary>
        /// <param name="m"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private Control CreateOneTopMenu(MenuModel m, int index)
        {
            var tm = new Label();
            tm.Width = 68;
            tm.Height = 40;
            tm.Text = m.MenuName;
            tm.TextAlign = ContentAlignment.MiddleCenter;
            tm.BackColor = Color.Transparent;
            tm.Left = index * 68 + index * 12 + 12;
            tm.Top = 9;
            tm.Cursor = Cursors.Hand;
            tm.Tag = m;
            tm.MouseEnter += new EventHandler(tm_MouseEnter);
            tm.MouseLeave += new EventHandler(tm_MouseLeave);
            tm.MouseUp += new MouseEventHandler(tm_MouseUp);
            return tm;
        }

对啦!顶部菜单就是一个label!

这些label创建出来之后,全部把他们放到TopMenuP这个panel了

这个panel就是顶部panel

tm.Left = index * 68 + index * 12 + 12;

这一句的作用是把这些顶级菜单依次排开~避免覆盖~

另外我把MenuModel的实例赋值给这个label的Tag属性了~后面有用

我为这些label注册了同样的鼠标划入、划出、弹起 事件

现在就看看这些事件

        /// <summary>
        /// 顶部菜单鼠标滑出
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void tm_MouseLeave(object sender, EventArgs e)
        {
            var lb = sender as Label;
            lb.BackColor = Color.Transparent;
            lb.ForeColor = SystemColors.ControlText;
        }
        /// <summary>
        /// 顶部菜单鼠标滑入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void tm_MouseEnter(object sender, EventArgs e)
        {
            var lb = sender as Label;
            lb.BackColor = Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(96)))), ((int)(((byte)(130)))));
            lb.ForeColor = Color.White;
        }
        /// <summary>
        /// 顶部菜单鼠标弹起
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void tm_MouseUp(object sender, MouseEventArgs e)
        {
            var lb = sender as Label;
            var m = lb.Tag as MenuModel;
            CreateSubMenu(m);
        }

顶部菜单划出和划入都没有什么特别的

只不过是改变了这个label的背景颜色和文字颜色

注意:这些颜色的值~应该放到资源或者缓存里去~

鼠标弹起事件~就说明客户点击了这个lable

我们把tag属性转换成MenuModel

然后就开始创建子菜单了

三:动态创建子菜单

代码如下

        /// <summary>
        /// 创建子菜单
        /// </summary>
        /// <param name="tm"></param>
        private void CreateSubMenu(MenuModel tm)
        {
            SubHeaderLB.Text = tm.MenuName;
            SubMenuP.Controls.Clear();
            var smms = (from v in Menus where v.ParentId == tm.Id orderby v.OrderNum select v).ToList();
            for (var i = 0; i < smms.Count; i++)
            {
                var ctl = CreateOneSubMenu(smms[i], i);
                SubMenuP.Controls.Add(ctl);
            }
        }
        /// <summary>
        /// 创建一个子菜单
        /// </summary>
        /// <param name="m"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        private Control CreateOneSubMenu(MenuModel m, int index)
        {
            var sm = new Label();
            sm.Width = SubHeaderLB.Width;
            sm.Height = 27;
            sm.Text = m.MenuName;
            sm.TextAlign = ContentAlignment.MiddleCenter;
            sm.BackColor = Color.Transparent;
            sm.Top = index * 27 + index * 9 + 9;
            sm.Left = 0;
            sm.Anchor = (System.Windows.Forms.AnchorStyles)(AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right);
            sm.Cursor = Cursors.Hand;
            sm.Tag = m;
            sm.MouseEnter += new EventHandler(sm_MouseEnter);
            sm.MouseLeave += new EventHandler(sm_MouseLeave);
            sm.MouseUp += new MouseEventHandler(sm_MouseUp);
            return sm;
        }

创建子菜单和创建顶部菜单~在原理上是一样的

也是用的label

sm.Anchor = (System.Windows.Forms.AnchorStyles)(AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right);

有了这一句子菜单的宽度会根着Left Panel的宽度的变化而变化

同时也注册了鼠标的滑入、滑出、弹起事件

弹起事件就是我们动态创建业务窗体的事件

我们放到后一节内容介绍

滑入和滑出的代码如下:

        /// <summary>
        /// 子菜单滑出
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void sm_MouseLeave(object sender, EventArgs e)
        {
            var lb = sender as Label;
            lb.BackColor = Color.Transparent;
        }
        /// <summary>
        /// 子菜单滑入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void sm_MouseEnter(object sender, EventArgs e)
        {
            var lb = sender as Label;
            lb.BackColor = SystemColors.Info;
        }

再次强烈要求

喜欢这篇文章或者喜欢我这个人的朋友~点推荐~点推荐~点推荐~点推荐~

 

 

 

 

 

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
3月前
|
容器 C# 开发者
XAML语言大揭秘:WPF标记的魅力所在,让你轻松实现界面与逻辑分离,告别复杂代码!
【8月更文挑战第31天】XAML提供了一种直观且易于维护的界面设计方式,使得开发者可以专注于逻辑和业务代码的编写,而无需关心界面细节。通过数据绑定、布局管理和动画效果等特性,XAML可以实现丰富的界面交互和视觉效果。在实际开发过程中,开发者应根据具体需求选择合适的技术方案,以确保应用程序能够满足用户的需求。希望本文的内容能够帮助您在WPF应用程序开发中更好地利用XAML语言。
43 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# 数据安全/隐私保护
C# 编写 WinForm 窗体应用程序(第三期)
文本框 (TextBox) 是在窗体中输入信息时最常用的控件,通过设置文本框属性可以实现多行文本框、密码框等。
C# 编写 WinForm 窗体应用程序(第三期)
C#编写WinForm窗体应用程序(第五期)
列表框 (ListBox) 将所提供的内容以列表的形式显示出来,并可以选择其中的一项或多项内容,从形式上比使用复选框更好一些。
C#编写WinForm窗体应用程序(第五期)
|
存储 算法 数据可视化
应用C#设计winform的一些心得
近期,因工作需要,应用C#设计了一个winform界面,主要是用来实现人员的量化积分管理,类似于很多单位的绩效考核管理系统那种。坦言之,这其实只是个人第二次涉猎winform窗体应用的设计(上一次还要追溯6-7年前的院校时期),上手还是比较慢的,前后大概花了10天时间。因为最后功能上还算比较满意,特写此文以作总结,记录当下。
602 0
应用C#设计winform的一些心得
|
开发框架 Oracle 关系型数据库
C/S架构Winform插件化框架,Winform通用界面框架
插件化框架特点: 1. 开发框架以模块化形式在逻辑上解耦 2. 开发框架模块以动态链接库(DLL文件)形式独立部署。 3. 模块主界面(frmBaseModule)用来分割系统功能菜单与功能按钮,作为各模块的入口界面。 4. 插件化框架核心功能-动态加载模块技术。
2247 0