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

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

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

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

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

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

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

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

另外

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

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

-----------

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;
}

再次强烈要求

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

 

 

 

 

 

posted @   liulun  阅读(8849)  评论(41编辑  收藏  举报
编辑推荐:
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
点击右上角即可分享
微信分享提示