Winform开发主界面菜单的动态树形列表展示
我在之前很多文章里面,介绍过Winform主界面的开发,基本上都是标准的界面,在顶部放置工具栏,中间区域则放置多文档的内容,但是在顶部菜单比较多的时候,就需要把菜单分为几级处理,如可以在顶部菜单放置一二级菜单,这种方式在一般功能点不算太多的情况下,呈现的界面效果较为直观、也较为美观。不过随着一些系统功能的增多,这种方式可能就会显得工具栏比较拥挤,那么我们是否可以在左侧放置一个树形列表,这样通过树形列表的收缩折叠,就可以放置非常多的菜单功能了。
1、菜单的树形列表展示
一般情况下,树形列表的显示可以分为多个节点,节点可以收缩也可以展开,当然节点是有不同的图标的了。这样就可以把很多功能点整合在一个树列表里面了,树的节点也可以分为很多级别,很多层次

如果我们想按照业务的范畴来区分,也可以分为多个模块展示,类似选项卡的方式,一个模块的功能菜单列表集合在一起展示,如下所示。

上面这样的折叠展示,有利于业务范畴的区分,并且可以让树菜单菜单不会很大,是一种比较好的界面组织方式。
2、菜单的动态配置管理
上面介绍了树形菜单的展示,以及如何组织菜单的内容,做好这些,就为我们奠定了界面菜单组织的雏形了。
那么问题来了,我们一般是需要根据系统创建很多菜单的,如果是能通过配置的方式,这样才能较好的管理这些菜单,而且可以动态给菜单指定权限,实现不同角色用户的权限控制。
那么我们就需要在系统里面引入一个菜单管理模块,实现菜单的配置管理功能,方便我们后面的动态创建菜单操作。

通过菜单的配置,我们可以指定菜单的图标,是否可见,是否展开,权限控制点,以及菜单触发点击后,处理的窗体对象等信息,有了这些基础信息,我们就很方便把菜单在树形列表里面进行合适、美观的展示了。
3、菜单动态构建的实现
前面介绍了,如何在数据库里面对菜单数据进行了存储,这样我们就可以在系统主界面里面,动态的构建属性列表进行菜单的展示操作了。
首先,我们需要在设计时刻对主界面的布局进行一定的设计,放置一些初始化的树形列表,方便查看效果。至于里面的内容,我们可以根据数据库的菜单配置,动态从数据库里面获取菜单信息,在左侧树形列表里面进行构建。

我们可以通过一个辅助类进行菜单的动态创建,如下所示。
private void InitToolbar()
{
TreeMenuHelper helper = new TreeMenuHelper(this, this.nvBarMenu, this.imageList1);
helper.Init();
}
也就是辅助类,传入当前窗体,以及左侧的导航控件等参数后,我们在辅助类里面封装对应的动态构建菜单的逻辑处理。
首先我们动态创建的开始,先要清空原来控件展示的菜单内容,并重新从数据库里面获取,如下代码所示。
//清空所有导航控件的内容
barControl.Controls.Clear();
barControl.Groups.Clear();
barControl.Items.Clear();
this.imageList.Images.Clear(); //限定显示几个导航选项卡
barControl.NavigationPaneMaxVisibleGroups = ; //约定菜单共有3级,第一级为大的类别,第二级为小模块分组,第三级为具体的菜单
List<MenuNodeInfo> menuList = BLLFactory<SysMenu>.Instance.GetTree(Portal.gc.SystemType);
if (menuList.Count == ) return;
然后我们会对菜单进行遍历,并判断是否具有对应的权限点,如果没有对应的权限,那么对应菜单的子菜单也不会进一步展示。
//递归遍历所有的菜单,进行分级展示
foreach (MenuNodeInfo firstInfo in menuList)
{
//如果没有菜单的权限,则跳过
if (!Portal.gc.HasFunction(firstInfo.FunctionId)) continue;
创建菜单的时候,我们注意到整个菜单项是动态构建的,因此我们需要根据NavBarControl的控件属性,动态构建对应的选项卡NavBarGroup、展示容器NavBarGroup、树形对象TreeView、树形节点TreeNode等内容,如下代码所示。
TreeView treeView = new TreeView();
treeView.Dock = DockStyle.Fill;
treeView.ImageList = this.imageList;
treeView.ItemHeight = ;//设置高度,显示更美观 NavBarGroupControlContainer container = new NavBarGroupControlContainer();
container.Size = new System.Drawing.Size(, );
container.Controls.Add(treeView);
barControl.Controls.Add(container); //加载图标
this.imageList.Images.Add(LoadIcon(firstInfo.Icon));
int index = this.imageList.Images.Count - ;//最后一个序号 NavBarGroup group = new NavBarGroup();
group.Caption = firstInfo.Name;
group.ControlContainer = container;
group.Expanded = true;
group.GroupClientHeight = ;
group.GroupStyle = NavBarGroupStyle.ControlContainer;
group.LargeImageIndex = index;
group.SmallImageIndex = index;
barControl.Groups.Add(group); //创建一级列表
TreeNode pNode = new TreeNode();
pNode.Text = firstInfo.Name;
pNode.Tag = firstInfo.WinformType;
pNode.ImageIndex = index;
pNode.SelectedImageIndex = index;
treeView.Nodes.Add(pNode); //递归创建子列表
AddTreeItems(pNode, firstInfo.Children);
通过递归的方式,我们就很容易递归构建了所有层次的树形菜单,并进行合适的展示了。
菜单的单击事件,我们通过一个函数代码实现对它进行处理就可以了。
//处理树形菜单的点击操作,如果TAG存在,则解析并加载对应的页面到多文档里面
treeView.AfterSelect += (sender, e) =>
{
string tag = e.Node.Tag as string;
if (!string.IsNullOrEmpty(tag))
{
LoadPlugInForm(tag);
}
};
这里面就是对它的AfterSelect 事件进行处理,实现我们动态加载窗体对象到多文档界面的处理了。
其中加载窗体是根据菜单配置的选项,动态构建界面出来的,具体分析代码如下所示。
/// <summary>
/// 加载插件窗体
/// </summary>
private void LoadPlugInForm(string typeName)
{
try
{
string[] itemArray = typeName.Split(new char[] { ',', ';' }); string type = itemArray[].Trim();
string filePath = itemArray[].Trim();//必须是相对路径 //判断是否配置了显示模式,默认窗体为Show非模式显示
string showDialog = (itemArray.Length > ) ? itemArray[].ToLower() : "";
bool isShowDialog = (showDialog == "") || (showDialog == "dialog"); string dllFullPath = Path.Combine(Application.StartupPath, filePath);
Assembly tempAssembly = System.Reflection.Assembly.LoadFrom(dllFullPath);
if (tempAssembly != null)
{
Type objType = tempAssembly.GetType(type);
if (objType != null)
{
LoadMdiForm(this.mainForm, objType, isShowDialog);
}
}
}
catch (Exception ex)
{
LogTextHelper.Error(string.Format("加载模块【{0}】失败,请检查书写是否正确。", typeName), ex);
}
}
加载多文档的操作,就是在集合里面判断是否存在,如果没有存在就创建,否则就激活显示即可,具体处理如下所示。
/// <summary>
/// 唯一加载某个类型的窗体,如果存在则显示,否则创建。
/// </summary>
/// <param name="mainDialog">主窗体对象</param>
/// <param name="formType">待显示的窗体类型</param>
/// <returns></returns>
public Form LoadMdiForm(Form mainDialog, Type formType, bool isShowDialog)
{
Form tableForm = null;
bool bFound = false;
if (!isShowDialog) //如果是模态窗口,跳过
{
foreach (Form form in mainDialog.MdiChildren)
{
if (form.GetType() == formType)
{
bFound = true;
tableForm = form;
break;
}
}
} //没有在多文档中找到或者是模态窗口,需要初始化属性
if (!bFound || isShowDialog)
{
tableForm = (Form)Activator.CreateInstance(formType); //如果窗体集成了IFunction接口(第一次创建需要设置)
IFunction function = tableForm as IFunction;
if (function != null)
{
//初始化权限控制信息
function.InitFunction(Portal.gc.LoginUserInfo, Portal.gc.FunctionDict); //记录程序的相关信息
function.AppInfo = new AppInfo(Portal.gc.AppUnit, Portal.gc.AppName, Portal.gc.AppWholeName, Portal.gc.SystemType);
} } if (isShowDialog)
{
tableForm.ShowDialog();
}
else
{
tableForm.MdiParent = mainDialog;
tableForm.Show();
}
tableForm.BringToFront();
tableForm.Activate(); return tableForm;
}
4、系统界面的总体效果
最后,为了更好理解整个动态菜单的界面效果,贴出几个做好的界面展示图,供参考学习。
1)标准界面的处理方式

2)树形列表界面的处理方式

打开多文档页面后如下所示。

Winform开发主界面菜单的动态树形列表展示的更多相关文章
- Winform开发框架主界面设计展示
做了好多年Winform的程序的开发,主窗口的界面设计一般都要求做的更好一些,可以根据不同的系统功能模块进行归类整合,能使客户迅速寻找到相关功能的同时,也能感觉到整体性的美观大方,因此主窗口的界面设计 ...
- Winform开发的界面处理优化
在Winform开发中,客户体验是个很好的参考性指标,如果一个功能使用的时候感觉很流畅,说明我们的程序执行效率还不错,但是随着数据的真多,原先可能流程的地方可能会变得比较卡,这时候就需要追本索源,找到 ...
- winform 防止主界面卡死
总结网络上的解决方案:新线程=> 委托=> 主界面的异步更新方法(IAsyncResult BeginInvoke(Delegate method)),一句话就是通过委托调用另一个线程的异 ...
- Winform开发中对界面的组织布局
在设计界面的时候,不管是在Web端,还是在Winform端,或者是WPF或者移动界面等应用上,我们对界面的组织布局,一直是比较有趣的话题,而组织界面的好坏从用户的感受来看,可以提供程序可使用性高低,也 ...
- 网络采集软件核心技术剖析系列(7)---如何使用C#语言搭建程序框架(经典Winform界面,顶部菜单栏,工具栏,左边树形列表,右边多Tab界面)
一 本系列随笔概览及产生的背景 自己开发的豆约翰博客备份专家软件工具问世3年多以来,深受广大博客写作和阅读爱好者的喜爱.同时也不乏一些技术爱好者咨询我,这个软件里面各种实用的功能是如何实现的. 该软件 ...
- Ajax-ajax实例3-动态树形列表
项目结构: 项目演示: 技术要点: 1.3.2 技术要点在基本原理的介绍中,了解到通过在父节点内动态创建子节点,并利用样式表缩进完成树形列表的基本框架.除了这一点外,还有下面一些问题需要考虑.1 .将 ...
- 在Winform界面菜单中实现动态增加【最近使用的文件】菜单项
在我们一些和文件处理打交道的系统中,我们往往需要记录下最近使用的文件,这样方便用户快速打开之前浏览或者编辑过的文件,这种在很多软件上很常见,本文主要介绍在Winform界面菜单中实现[最近使用的文件] ...
- 如何快速开发树形列表和分页查询整合的WInform程序界面
我在做Winform界面的时候,一般都是统一化处理,界面顶部放置一些字段条件供查询,下面就是分页查询列表,展示相关的数据.但有时候碰到一些表字段内容分类比较多,有一些特别重要,如果放在一个树形列表来进 ...
- ABP开发框架前后端开发系列---(11)菜单的动态管理
在前面随笔<ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理>中介绍了基于ABP框架服务构建的Winform客户端,客户端通过Web API调用的方式进行获取数据,从而实现 ...
随机推荐
- 【译】用Fragment解决屏幕旋转(状态发生变化)状态不能保持的问题
这篇文章解决了在StackOverflow上一个经常被提到的问题. 在配置发生变化(Configuration changs)时,什么是最好的保存活动对象方法,比如运行中的线程,Sockets,Asy ...
- 更改Photoshop 语言为英语(无需语言包)
因为有时看国外教程时,手头上的PS是中文的而教程里的界面是英文的,而且中英菜单顺序在某些地方是不一样的,所以很不方便. 终于找到一个非常完美的方法可以把界面换成英文,而且不需任何语言包. 并且试了在最 ...
- 在此记录一下SharpGL最初创建的程序
在此记录一下SharpGL最初创建的程序.完整工程在此. /// <summary> /// The main form class. /// </summary> publi ...
- vmware安装ubuntu12.04嵌套安装xen server(实现嵌套虚拟化)
环境准备 软件:vmware workstation 9.0 ubuntu-12.04.2-server-amd64(官方下载) 硬件:确认CPU支持虚拟化VM-T vmware设置 vmwar ...
- js笔记——理解js中的call及apply
call及apply在js里经常碰得到,但一直感觉很陌生,不能熟练使用.怎样才能熟练应用呢? 为什么存在call和apply? 在javascript OOP中,我们经常会这样定义: function ...
- PHP面向对象06_异常处理
oop06异常处理 2014-9-2 8:36:33 NotePad++ By jiancaigege 摘要: 1.异常处理 2.类中常用函数 异常处理 语法格式: try{ //捕获异常 }catc ...
- 关于STM32的外部引脚中断的问题
今天想用自己以前的比较干净的工程模板做一个东西,,,,,,,在添加上引脚中断的时候,,突然想知道自己配置的中断优先级是否正确执行,,,,, 以前刚学习32的时候测试过是可以的,,不过今天发现了一个大问 ...
- salesforce 零基础开发入门学习(十四)salesforce中工厂模式的运用
提到工厂模式,想必大家都很熟悉,工厂模式作为一种设计模式,同样在salesforce中适用. 举一个例子,笔作为基类,可以有钢笔,铅笔,圆珠笔等等.有一个笔的工厂,当你向它要钢笔,它就会生产一支钢笔; ...
- CCNA网络工程师学习进程(6)vlan相关协议的配置与路由器简单配置介绍
前面已经介绍了大部分与vlan技术相关的交换机的协议的配置,更深层次的还有STP协议和以太网端口聚合技术,接着还会简单介绍一下路由器的基本应用. (1)STP(Spanning-tre ...
- 浏览器兼容处理(HTML条件注释、CSSHack和JS识别)
前面的话 本文中所有IEx+代表包含x及x以上:IEx-代表包含x及x以下,仅个人习惯.例:IE7+代表IE7.IE8…… 本文中所有例子全部经过测试,欢迎交流. HTML识别 条件注释法(IE10+ ...