这个月做项目,遇到过一个通过配置文件来生成菜单的解决方案,感觉挺优雅的,特地放到博客园来,以飨读者。

说来惭愧,以前做的项目都没有这样用过,都是固定死了。如果后续有需要加入菜单,还得在重新修改UI,然后提交code,才OK。但是如果通过配置文件的方式进行操作,则可以不用动code,只需要修改配置文件即可。

有兴趣的可以往下看,不难,很简单!~

先看下这次demo的结构:

1。生成XML式的配置文件菜单项。。

我用XML格式来存储,

<MenuList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<language>zh-ch</language>
<displayName>Simplified_Chinese</displayName>
<version>1.0</version>
<author>Crystal</author>
<MenuItem Name="Trade" HeaderText="交易" MenuId="1" ParentMenuId="0">
<MenuItem Name="CommodityTrade" HeaderText="商品交易" MenuId="11" ParentMenuId="1" FormName="TestMenu.Menu.MenuForm.Trade.CommodityTrade" MenuName="TestMenu.Menu.MenuLogical.Trade.CommodityTradeMenu" OpenType="0"></MenuItem>
<MenuItem Name="ConditionTrade" HeaderText="条件下单" MenuId="12" ParentMenuId="1" FormName="TestMenu.Menu.MenuForm.Trade.ConditionTrade" MenuName="TestMenu.Menu.MenuLogical.Trade.ConditionTradeMenu" OpenType="1"></MenuItem>
<MenuItem Name="DeliveryTrade" HeaderText="交割申报" MenuId="13" ParentMenuId="1" FormName="TestMenu.Menu.MenuForm.Trade.DeliveryTrade" MenuName="TestMenu.Menu.MenuLogical.Trade.DeliveryTradeMenu" OpenType="1"></MenuItem>
</MenuItem>
<MenuItem Name="Bank" HeaderText="银行" MenuId="2" ParentMenuId="0">
<MenuItem Name="BankSign" HeaderText="银行签约" MenuId="21" ParentMenuId="2"></MenuItem>
<MenuItem Name="BankUnSign" HeaderText="银行解约" MenuId="22" ParentMenuId="2"></MenuItem>
</MenuItem>
<MenuItem Name="Fund" HeaderText="资金" MenuId="3" ParentMenuId="0">
<MenuItem Name="FundInOut" HeaderText="出入金" MenuId="31" ParentMenuId="3"></MenuItem>
<MenuItem Name="FundTransfer" HeaderText="资金转移" MenuId="32" ParentMenuId="3"></MenuItem>
</MenuItem>
<MenuItem Name="Help" HeaderText="帮助" MenuId="9" ParentMenuId="0">
<MenuItem Name="HelpHotKey" HeaderText="热键帮助" MenuId="91" ParentMenuId="9" FormName="" MenuName="" OpenType="0"></MenuItem>
<MenuItem Name="HelpNormalQues" HeaderText="常见问题" MenuId="92" ParentMenuId="9" FormName="" MenuName="" OpenType="0"></MenuItem>
<MenuItem Name="HelpTelphone" HeaderText="电话服务" MenuId="93" ParentMenuId="9" FormName="" MenuName="" OpenType="0"></MenuItem>
<MenuItem Name="HelpProductBook" HeaderText="产品说明书" MenuId="94" ParentMenuId="9" FormName="" MenuName="" OpenType="0"></MenuItem>
<MenuItem Name="HelpAbout" HeaderText="关于产品" MenuId="95" ParentMenuId="9"></MenuItem>
</MenuItem>
</MenuList>

通过上面我们知道,这是一个标准的XML格式文件。

ParentMenuId=0表示这是父类,也就是一级菜单!如果是“1”则表示二级菜单,其它的余此类推。

FormName:表示窗体的路径

MenuName:表示这个窗体所对于的后台逻辑代码!这是一个类继承MenuBase类!

MenuID:就是一个每个Menu的ID,在MenuName子类的时候,需要指定。

2。两个实体类:MenuInfo和MenuItemInfo

MenuInfo:

 [Serializable]
[XmlRoot("MenuList")]
public class MenuInfo
{
[XmlElement("language")]
public string Language { get; set; } [XmlElement("displayName")]
public string DisplayName { get; set; } [XmlElement("version")]
public string Version { get; set; } [XmlElement("author")]
public string Author { get; set; } [XmlElement("MenuItem")]
public List<MenuItemInfo> MenuItemList { get; set; }
}

MenuItemInfo:

 [Serializable]
public class MenuItemInfo
{
[XmlAttribute("Name")]
public string Name { get; set; } [XmlAttribute("HeaderText")]
public string HeaderText { get; set; } [XmlAttribute("MenuId")]
public int MenuId { get; set; } [XmlAttribute("ParentMenuId")]
public int ParentMenuId { get; set; } [XmlAttribute("FormName")]
public string FormName { get; set; } [XmlAttribute("MenuName")]
public string MenuName { set; get; } [XmlAttribute("OpenType")]
public int OpenType { get; set; } [XmlElement("MenuItem")]
public List<MenuItemInfo> MenuItemList { get; set; }
} public enum OpenType
{
NewForm = , //新窗口打开
TabForm = , //Tab页打开
}

就2个Model很简单,字段都是跟XML文件里的字段相对应!

3.。所有Menu的基类---MenuBase

先看代码:

public class MenuBase
{
public MenuItemInfo menuItemInfo;
public MenuBase()
{
}
public MenuBase(int menuId)
{
this.menuItemInfo = BaseLogic.GetInstanse().MenuList.Find(p => p.MenuId == menuId);
}
public virtual void Excute(TabControl tabControl)
{
if (!string.IsNullOrEmpty(this.menuItemInfo.FormName))
{
if (this.menuItemInfo.OpenType == (int)OpenType.NewForm) //新窗口打开
{
Window billForm = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(this.menuItemInfo.FormName, false) as Window;
billForm.ShowDialog();
billForm.Close();
}
else if (this.menuItemInfo.OpenType == (int)OpenType.TabForm) //Tab页打开
{
CloseableTabItem tab = null;
// TabItem tab = null;
if (tabControl.Items.Count > )
{
foreach (CloseableTabItem item in tabControl.Items)
{
if (item.Name == this.menuItemInfo.Name)
{
tab = item;
break;
}
}
}
if (tab == null)
{
var v = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(this.menuItemInfo.FormName, false);
//tab = new TabItem();
tab = new CloseableTabItem();
tab.Name = this.menuItemInfo.Name;
tab.Header = this.menuItemInfo.HeaderText;
//tab.Tag = this.menuItemInfo.MenuName;
tab.Content = v; tabControl.Items.Add(tab);
}
tab.IsSelected = true;
}
}
}
}

这是一个关键基类,每一个菜单所对应的后台逻辑页面,都要继承MenuBase基类!~

通过上面介绍XML的时候,我们知道有一个字段是“MenuID”。这个MenuID就是在实现MenuBase的时候,通过base 传入到基类。然后基类通过下面code进行查询到相对于页面信息:

this.menuItemInfo = BaseLogic.GetInstanse().MenuList.Find(p => p.MenuId == menuId);

Execute是一个虚方法,子类进行调用。它有一个参数就是TabControl,如果OpenType(一个枚举值,是一个窗体还是一个Tab)的是一个Tab值,那么就把这个页面加入到TabControl控件中。否则,弹出框!

应该说以上就是一些核心的代码。

4.。怎么用呢?

怎么使用呢?很简单,在我们的XAML中 放入如下东东:

  <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30px" />
<RowDefinition />
</Grid.RowDefinitions>
<Menu Name="MainMenu">
</Menu>
<TabControl Name="tabDown" TabStripPlacement="Bottom" Grid.Row="1">
</TabControl>
</Grid>

后台:

 public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
//加载菜单!
if (BaseLogic.GetInstanse().MenuList != null && BaseLogic.GetInstanse().MenuList.Count > )
{
BindMenu(, null);
}
}
private void BindMenu(int parentId, MenuItem item)
{
List<MenuItemInfo> list = BaseLogic.GetInstanse().MenuList.FindAll(p => p.ParentMenuId == parentId);
if (list != null && list.Count > )
{
foreach (MenuItemInfo info in list)
{
MenuItem menuItem = new MenuItem();
menuItem.Name = info.Name;
menuItem.Header = info.HeaderText;
menuItem.Tag = info;
menuItem.Click += new RoutedEventHandler(menuItem_Click);
if (parentId == )
{
this.MainMenu.Items.Add(menuItem);
}
else
{
item.Items.Add(menuItem);
}
BindMenu(info.MenuId, menuItem);
}
}
} private void menuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem item = sender as MenuItem; string menuName = ((MenuItemInfo)(item.Tag)).MenuName;
//string fullName = menuName;
//MenuBase menuBase = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(string.Format("ygPlatForm.Logic.MenuLogic.{0}Menu", item.Name), false) as MenuBase;
if (menuName != null)
{
MenuBase menuBase = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(menuName, true) as MenuBase;
if (menuBase != null)
menuBase.Excute(this.tabDown);
}
}
}

就是一些绑定操作!~

5.。结果

匆忙写的,不懂的可以在讨论哈!~晚安咯 各位大神!

猛击源码下载

WPF+通过配置文件生成菜单(Menu)+源码的更多相关文章

  1. Ocelot简易教程(七)之配置文件数据库存储插件源码解析

    作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9852711.html 上篇文章给大家分享了如何集成我写的一个Ocelot扩展插件把Ocelot的配置存储 ...

  2. 开源方案搭建可离线的精美矢量切片地图服务-8.mapbox 之sprite大图图标文件生成(附源码)

    项目成果展示(所有项目文件都在阿里云的共享云虚拟主机上,访问地图可以会有点慢,请多多包涵). 01:中国地图:http://test.sharegis.cn/mapbox/html/3china.ht ...

  3. H5类似易企秀/编辑器/页面制作/开发/生成工具/软件/源码/授权

    代码地址如下:http://www.demodashi.com/demo/14960.html 项目简介 H5DS (HTML5 Design software) 这是一款基于WEB的 H5制作工具. ...

  4. C#/ASP.NET MVC微信公众号接口开发之从零开发(四) 微信自定义菜单(附源码)

    C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...

  5. jQuery实现多级手风琴树形下拉菜单(源码)

    前几天因为公司的菜单要调整,公司的UI框架是不支持的,所以就自己在网上找了一个下拉菜单,可以支持多级菜单数据的,菜单数据是从xml文件中配置后读取的,网上有许多这方面的例子感觉不是很好用,就打了个包贴 ...

  6. 【krpano】二维码自动生成插件(源码+介绍+预览)

    简介 在krpano生成的全景支持HTML5在手机中展示,而在手机中打开全景网址时不方便,需要输入网址. 最近研究了如何让krpano全景根据自己当前的网址,自动生成二维码,并在电脑浏览时,可以展示出 ...

  7. 轻量级验证码生成插件webutil-licenseImage源码与实例应用

    webutil-licenseImage 插件内置4种验证码样式,支持用户扩展.自定义样式实现简单验证码. 源码脱管地址: http://code.google.com/p/licenseimage/ ...

  8. python生成可执行文件保护源码

    工作中由于需要防止源代泄漏,需要将源代码隐藏,找到两种方法: 1.使用python生成的pyc文件. 这种方法的优点就是pyc文件生成很容易,缺点则是很容易通过工具得到源码,并且python版本不一致 ...

  9. Spark streaming技术内幕6 : Job动态生成原理与源码解析

    原创文章,转载请注明:转载自 周岳飞博客(http://www.cnblogs.com/zhouyf/)  Spark streaming 程序的运行过程是将DStream的操作转化成RDD的操作,S ...

随机推荐

  1. Linux 2.6内核中新的锁机制--RCU

    转自:http://www.ibm.com/developerworks/cn/linux/l-rcu/ 一. 引言 众所周知,为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁 ...

  2. 优化Linux内核参数

    转自:http://www.centoscn.com/CentOS/config/2013/0804/992.html vim /etc/sysctl.conf 1.net.ipv4.tcp_max_ ...

  3. 《Android NFC 开发实战详解 》简介+源码+样章+勘误ING

    <Android NFC 开发实战详解>简介+源码+样章+勘误ING SkySeraph Mar. 14th  2014 Email:skyseraph00@163.com 更多精彩请直接 ...

  4. Kinect开发随笔①——红外扫描仪(Kinect 数据源)

    来源于 MVA 的 快速入门:Kinect for Windows v2 开发 的学习随笔 具体内容为上图所示章节内容 章节内全部代码:GitHub地址点我(链接失效,待补档) <Page &l ...

  5. vue学习之旅

    大纲: 属性 事件 循环 指令 交互 过滤器 模板 计算属性 自定义过滤器和指令 组件(父子组件之间的通讯) 路由和多层路由以及占位槽slot等其他 vue-loader和模块加载(webpack)等 ...

  6. [算法总结]partition (quicksort)

    private int partition(int[] nums, int lo, int hi) { if (lo >= hi) { return lo; } int i = lo; int ...

  7. pythony语法小练习

    i=[0,1] b=[2,3] print(dict([i,b]))#列表转换为字典 题目:有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少? 程序分析:可填在百位.十位 ...

  8. WEB开发入门

    对服务器的概念需要更新一下: 从物理上来说,服务器就是一台PC机,至少8核,以T计算,带宽100M以上 一般有的服务器 1. web服务器 -- PC机上安装一个具有web服务的软件 2. 数据库服务 ...

  9. phoneGap

    1.安装参考http://blog.csdn.net/mage694/article/details/16846331 2.API  参考http://phonegap.com/developer/

  10. servlet中session的使用

    1.获取session HttpSession session=request.getSession(); session.setAttribute("variety", vari ...