wpf 菜单样式和绑定树形数据
前言
在wpf开发中,经常会使用到Menu和ContentMenu。但是原生的样式比较简陋,对于比较追求界面美好的人来说是十分不友好的。那么,这就涉及到对Menu的样式修改了。与此同时,我们还希望Menu自动Binding到视图数据模型上,根据数据项自动展开MenuItem。接下来就对这些想法做一简单实现。
视图模型
假设我们的菜单项里有描述意图的缩略图和文字需要展示。那么我们需要有名字和存有图片路径的属性。额外的,还需要一个Children集合来存放子项,以形成树形数据。
视图模型Class
public class CommonTreeModel
{
/// <summary>
/// 名字
/// </summary>
public string Name { get; set; }
/// <summary>
/// 图片的路径
/// </summary>
public string IconPath { get; set; }
/// <summary>
/// 子项
/// </summary>
public ObservableCollection<CommonTreeModel> Children { get; set; }
}
数据mock
我们使用Bogus进行模拟数据的产生。使用nuget搜索Bogus添加即可。
/// <summary>
/// 菜单项数据集,前端将binding到该属性上
/// </summary>
public ObservableCollection<CommonTreeModel> MenuTreeSource { get; set; }
private void InitData()
{
var general = new Bogus.Faker<CommonTreeModel>()
.RuleFor(t => t.Name, t => t.Commerce.Product())//名字:商业产品
.RuleFor(t => t.IconPath, t => t.Image.LoremFlickrUrl(32, 32));//图片:使用LoremFlick网站的图片
var rd = new Random(DateTime.Now.Millisecond);//随机数
MenuTreeSource = GenerateTreeData(general, rd, 10, 3, 10);
}
private ObservableCollection<CommonTreeModel> GenerateTreeData(Faker<CommonTreeModel> faker, Random rd, int topCount, int subMin, int subMaxm, int level = 0, int levelLimit = 4)
{
var list = new ObservableCollection<CommonTreeModel>(faker.Generate(level == 0 ? topCount : rd.Next(subMin, subMaxm)));
level++;
if (level < levelLimit)
{
foreach (var item in list)
{
if (rd.Next() % 2 == 0)
{
item.Children = GenerateTreeData(faker, rd, topCount, subMin, subMaxm, level, levelLimit);
}
}
}
return list;
}
到这里,我们生成了最大层级可能为4的一棵树:MenuTreeSource。
WPF中的Menu
我们先写前端Menu绑定到数据。
<Menu ItemsSource="{Binding MenuTreeSource}" HorizontalAlignment="Center">
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Label Content="{Binding Name}"></Label>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
原始的样式展示效果如下,总共有四级,并且第一级和后三级不一致。

MenuItem样式
我们只需重写MenuItem的样式,就能够改变菜单的展示效果:
<Style TargetType="MenuItem">
<Setter Property="FontSize" Value="20"></Setter>
<Setter Property="Foreground" Value="#b8d00a"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="#b8d00a"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#f46a56"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Width="32" Height="32" Margin="5" Source="{Binding IconPath}" />
<Label Content="{Binding Name}" Margin="10 0" Grid.Column="1" VerticalContentAlignment="Center"/>
<Label Name="MoreLbl" Content=">>" Grid.Column="2" VerticalContentAlignment="Center">
<Label.Style>
<Style TargetType="Label">
<Setter Property="Visibility" Value="Visible"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Children}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Popup AllowsTransparency="True"
IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}"
Placement="Right" x:Name="SubMenuPopup" Focusable="false">
<Border x:Name="SubMenuBorder" BorderThickness="1" BorderBrush="Black">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/>
</Border>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
控件Popup用于展示子级菜单项。其中StackPanel上的IsItemsHost="True"保证正确处理子级。
名为MoreLbl的Label控件用于提示是否有子级。这里简略用>>标识一下,读者可以图片Image或者Path控件做一个漂亮的样式。
最终效果如下,一级和次级依然不统一。

统一Menu的一级和次级
为了统一,我们需要对Menu的样式加以调整。最简单的方式是对子项承载容器进行替换,我们用StackPanel控件,并把内容排序设置为垂直方向即可。
<Menu ItemsSource="{Binding MenuTreeSource}" HorizontalAlignment="Center">
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"></StackPanel>
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}"></HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
最终效果如下:

WPF中的ContextMenu控件
ContextMenu与Menu不同,他可以作为很多控件的Popup形式的鼠标右键弹出菜单,比如作为ListBox,Label,Grid等的内容菜单都是可以的,我们只需鼠标右键就可以将其弹出。
我们用Label简单举例:
<Label Content="鼠标右键弹出ContextMenu" HorizontalAlignment="Center"HorizontalContentAlignment="Center" FontSize="25" Background="#221a12"Foreground="#b8d00a"
MouseDown="Label_MouseDown">
<Label.ContextMenu>
<ContextMenu ItemsSource="{Binding MenuTreeSource}">
<ContextMenu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" />
</ContextMenu.ItemTemplate>
</ContextMenu>
</Label.ContextMenu>
</Label>
之前的样式都是全局样式,默认会自动使用。效果如下:

能够发现一级的样式稍微有些不一样,我们需要改ContextMenu的样式
<Style TargetType="{x:Type ContextMenu}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Grid.IsSharedSizeScope" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border
BorderBrush="Black"
BorderThickness="1" >
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
简单重写Template即可。

总结
这里只简单介绍如何去重写样式模板来改变菜单展示效果。具体使用中大家需要更好看的效果请自行设计。该wpf项目框架使用dotnet core3.1版本。在farmwork中应该一样。
wpf 菜单样式和绑定树形数据的更多相关文章
- WPF (DataGridRowHeaderStyle)实现自义定行样式 并绑定数据
原文:WPF (DataGridRowHeaderStyle)实现自义定行样式 并绑定数据 功能阐述 就上面那图片 刚开始 考虑使用 RowHeaderTemplate 来实现 发现总绑定不上数据 ...
- WPF (DataGridColumnHeader)实现自义定列头样式 并绑定数据
原文:WPF (DataGridColumnHeader)实现自义定列头样式 并绑定数据 实现功能是这样的 自定义列头 样式 样式里的 数据来源于后台绑定 这篇就说头样式 和头样式数据绑定 思路 1) ...
- [WPF 基础知识系列] —— 绑定中的数据校验Vaildation
前言: 只要是有表单存在,那么就有可能有对数据的校验需求.如:判断是否为整数.判断电子邮件格式等等. WPF采用一种全新的方式 - Binding,来实现前台显示与后台数据进行交互,当然数据校验方式也 ...
- WPF——绑定数据库数据(Listview)
一.首先先画一个窗体,放进一个Listview 然后给每列起好名字,并且绑定的数据是临时表的列名 二.造一个临时表用来存储数据,并且将扔进去的Listview绑定到这个临时表DataTable上面 p ...
- wpf:样式(转)
http://www.cnblogs.com/shuang121/archive/2013/01/14/2860455.html 前面简单的说到了wpf中几种样式的用法,wpf有着类似web中的CSS ...
- Web中树形数据(层级关系数据)的实现—以行政区树为例
在Web开发中常常遇到树形数据的操作,如菜单.组织机构.行政区(省.市.县)等具有层级关系的数据. 以下以行政区为例说明树形数据(层级关系数据)的存储以及实现,效果如图所看到的. 1 数据库表结构设计 ...
- wpf 导出Excel Wpf Button 样式 wpf简单进度条 List泛型集合对象排序 C#集合
wpf 导出Excel 1 private void Button_Click_1(object sender, RoutedEventArgs e) 2 { 3 4 ExportDataGrid ...
- WPF获得PNG图片外观Path数据
原文:WPF获得PNG图片外观Path数据 WPF开发界面的时候,用的最多的就是自定义控件模板,开发人员需要根据UI的设计,做出符合要求的自定义控件.但是在一些特殊情况下,UI的设计可能 ...
- winform快速开发平台 -> 快速绑定ComboBox数据控件
通常我们在处理编辑窗体时.往往会遇到数据绑定.例如combobox控件绑定数据字典可能是我们经常用到的.然而在我的winform快速开发平台中我是如何处理这个频繁的操作呢? 首先,我们要绑定combo ...
随机推荐
- 吴裕雄--天生自然操作系统操作笔记:window10显示隐藏文件夹
基于安全考虑,操作系统会隐藏一些文件和文件夹,防止误删除操作.但有可能是个别人为了隐藏一些私密数据,也同样采取隐藏的方式.
- tftpd64-SE使用
使用场景: 把windows下的文件写入到linux(嵌入式设备中): 下载地址: https://bitbucket.org/phjounin/tftpd64/wiki/Download%20Tft ...
- Apache2配置腾讯云SSL证书
首先去腾讯云申请免费的SSL证书,下载下来解压后里面有一个Apache文件夹,里面有三个文件,接下来会用到. 上传证书 将上一步的三个文件上传到/etc/ssl里 启用SSL模块 启用a2enmod ...
- 红杉资本的Dropbox上市,国内哪些产品会跟着受益?
每一个估值达到10亿美元以上的互联网.科技独角兽企业,都将上市当做"终极荣光".但事实上,上市只是这些独角兽企业开启全新时代的开端而已.很多气势汹汹且看似前景一片光明的独角兽 ...
- spring+mybatis+mysql5.7实现读写分离,主从复制
申明:请尽量与我本博文所有的软件版本保持一致,避免不必要的错误. 所用软件版本列表:MySQL 5.7spring5mybaties3.4.6 首先搭建一个完整的spring5+springMVC5+ ...
- Java volatile修饰字段
一.关键字volatile修饰字段: 使用特殊域变量(volatile)实现线程同步 volatile:不稳定的:反复无常的:易挥发的: 1.volatile关键字为域变量的访问提供了一种免锁机制, ...
- iOS多线程开发之GCD(死锁篇)
上篇和中篇讲解了什么是GCD,如何使用GCD,这篇文章将讲解使用GCD中将遇到的死锁问题.有兴趣的朋友可以回顾<iOS多线程开发之GCD(上篇)>和<iOS多线程开发之GCD(中篇) ...
- 「知乎」对中国用户而言,Pure Android 是否比 MIUI 或 Flyme 体验更好? - Donnie的博客
这篇文章转载自我在知乎上的回答 哎呀-不要站队嘛.其实这是一个很有意思的题目,让我们一点点来看 哦对,谢妖-.本人是Nexus 5用户,系统当然是Pure Android KitKat啦(臭谷粉!点D ...
- 2、【Spark】Spark环境搭建(集群方式)
Spark集群方式搭建结构如图所示,按照主从方式.
- jQuery样式及html属性操作
样式及html属性操作1,行内样式 css(); a:获取样式 元素.css(样式名称); b:设置单个样式 元素.css("样式名称":"样式值"); c:设 ...