先看一下效果吧:

我们直接通过改造一下原版的TreeView来实现上面这个效果

我们先创建一个普通的TreeView

代码很简单:

        <TreeView>
<TreeViewItem Header="人事部"/>
<TreeViewItem Header="技术部">
<TreeViewItem Header="技术部-1"/>
<TreeViewItem Header="技术部-1"/>
</TreeViewItem>
<TreeViewItem Header="财务部"/>
</TreeView>

实现的效果如下:

如果把这个当成是项目的菜单栏,应该会被领导骂死,一个是不够灵活,数据是写死的;二是样式不好看,只有点文字部分才会展开。

创建一下模板

直接在设计器中右键我们的item,编辑副本,点击确定,我们会得到下面一段代码

里面有一个叫Bd的border,我们把这个border的背景色去掉,然后我们自己去创建两个新的border

<Border Background="Transparent" Margin="-200,0,-200,0" Grid.ColumnSpan="4"/>
<Border x:Name="bd1" Background="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"
Margin="-200,0,-200,0" Visibility="Hidden" Grid.ColumnSpan="4">
<Border.Effect>
<DropShadowEffect BlurRadius="5" ShadowDepth="2"/>
</Border.Effect>
</Border>
<ToggleButton x:Name="Expander" ClickMode="Press"
IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Style="{StaticResource ExpandCollapseToggleStyle}"/>
<Border x:Name="Bd" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>

上面红色部分是我们新增的两个border,原本的叫Bd的border,我们只保留紫色部分的属性.

原本的代码里面有两个关于Bd的trigger

我们取名为bd1的border,最开始的Visibility设置的是Hidden,我们替换一下关于Bd的trigger,让它变成当IsSelected是true的情况下,让bd1的Visibility变成Visible.

<Trigger Property="IsSelected" Value="true">
<Setter Property="Visibility" TargetName="bd1" Value="Visible"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Visibility" TargetName="bd1" Value="Visible"/>
<Setter Property="Background" TargetName="bd1" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}}"/>

再运行一下,看一下效果

基本上已经算是成功一半了,但是这个时候,我们的菜单只有一个有效果,其他的还是原来的样式,那是因为我们只有一个TreeViewItem使用了我们写的效果

如果我们每一个TreeViewItem都复制一下这句Style="{DynamicResource TreeViewItemStyle1}" ,是不是显得很呆,而且这只是在我们的菜单很少的情况下,如果菜单很多,这个方法就不可行。

所以这里我们用一个TreeView的ItemContainerStyle来操作一下

        <Style x:Key="treeViewStyle1" TargetType="{x:Type TreeView}" BasedOn="{StaticResource {x:Type TreeView}}">
<Setter Property="ItemContainerStyle" Value="{StaticResource TreeViewItemStyle1}"/>
</Style>

我们创建一个类型是TreeView的style,把它的ItemContainerStyle设置成我们之前添加的那个style,然后我们把这个style放到我们的TreeView上

这个时候我们再运行就会发现首级菜单的样式都实现我们想要的效果了,但是子集菜单还是原来的样式

我们在代码里面添加下面一个方法

        private void ApplyItemContainerStyle(ItemsControl itemsControl)
{
foreach (var item in itemsControl.Items)
{
var treeViewItem = item as TreeViewItem;
if (treeViewItem != null)
{
treeViewItem.Style = treeview1.ItemContainerStyle;
ApplyItemContainerStyle(treeViewItem);
}
}
}

然后我们在构造函数里面把我们的TreeView当做是参数传进去

这个方法就是把所有的item和item的子项都设置成treeview的ItemContainerStyle;

我们再启动一下项目,就会发现效果是我们想要的效果了

到这里其实大部分效果都实现了,基本上也可以向领导交差了;

但是还缺少一个数据可拓展性和一个图标的功能,我们先看一下数据可拓展性

在平时的项目里面,一般都会有很多个不同的项目,每个项目可能都有好多个菜单,有的项目还想隐藏某一些菜单,我们总不能所有项目都通过visible属性来设置吧

特别是报表功能可能会有几十个,所以我们需要用到一个东西叫数据模板:HierarchicalDataTemplate;

我们先创建一个类

    public class TreeViewModel
{
public string Header { get; set; }
public ObservableCollection<TreeViewModel> Children { get; set; }
}

然后回到设计器里面,把我们的代码改成下面的代码

<TreeView Style="{DynamicResource treeViewStyle1}" x:Name="treeview1">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TreeViewModel}" ItemsSource="{Binding Children}">
<StackPanel Height="40" Orientation="Horizontal">
<TextBlock Text="{Binding Header}" VerticalAlignment="Center"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

对比一下红色部分的绑定,和类的属性,就能知道这个数据模板怎么用了.

再到构造函数里面去添加数据

public ObservableCollection<TreeViewModel> MenuCollection { get; set; }

public MainWindow()
{
InitializeComponent(); MenuCollection = new ObservableCollection<TreeViewModel>()
{
new TreeViewModel
{
Header = "人事部"
},
new TreeViewModel
{
Header = "技术部",
Children = new ObservableCollection<TreeViewModel>
{
new TreeViewModel { Header = "技术部-1"},
new TreeViewModel { Header = "技术部-2"},
}
},
new TreeViewModel
{
Header = "财务部",
},
}; treeview1.ItemsSource = MenuCollection;
}

注意这两段标红的代码,我们用一个集合MenuCollection模拟一下我们从数据库或者其他地方查询出来的菜单集合,然后把它做为数据源给treeview就可以了

再运行一下项目,它就差不多实现我们想要的效果了,现在再去找领导交差,领导还会夸你做的不错,只是还差一个图标了,这个就是锦上添花的东西了.

我们百度搜索一下  阿里ICON,去到官网里面,创建一个自己的账号,然后搜索一些自己喜欢的图标

把自己喜欢的图标添加到自己的项目中去,这里的项目名很重要,我取的是  FatSheep

在到我的项目里面去把这个资源文件下载到自己的项目中

下载下来的文件,我们把ttf后缀的文件添加到我们的项目里面去

把它作为资源引入到代码里面

<FontFamily x:Key="FatSheep">pack:application:,,,/自己项目的名字;component/Resources/iconfont.ttf#FatSheep</FontFamily>

记得修改一下自己的项目名字,我取的是TreeViewDemo,改成自己的项目名就好了,最后的结尾,是FatSheep,记得改成自己的ICON项目名称

接着我们在TreeViewModel里面添加一个Icon属性

    public class TreeViewModel
{
public string Header { get; set; }
public string Icon { get; set; }
public ObservableCollection<TreeViewModel> Children { get; set; }
}

然后我们在数据源里面添加一下数据

MenuCollection = new ObservableCollection<TreeViewModel>()
{
new TreeViewModel
{
Header = "人事部",
Icon = "\ue71c"
},
new TreeViewModel
{
Header = "技术部",
Icon = "\ue71c",
Children = new ObservableCollection<TreeViewModel>
{
new TreeViewModel { Header = "技术部-1", Icon="\ue71c"},
new TreeViewModel { Header = "技术部-2" , Icon="\ue71c"},
}
},
new TreeViewModel
{
Header = "财务部",
Icon = "\ue71c"
},
};

设计器里面添加一下显示部分的代码

<TreeView Style="{StaticResource treeViewStyle1}" x:Name="treeView1" BorderThickness="0,0,1,0" Grid.Column="1">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TreeViewModel}" ItemsSource="{Binding Children}">
<StackPanel Height="40" Orientation="Horizontal">
<TextBlock Text="{Binding Icon}" VerticalAlignment="Center" FontFamily="{StaticResource FatSheep}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Header}" VerticalAlignment="Center"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

再启动项目,功能就完成了

这个笑脸是怎么来的了

那是因为我自己的项目里面添加了一个笑脸

我们复制一下这个代码,     我们把它改成 \ue71c,这是一个转义字符,就这样我们就能添加如何自己喜欢的图标了。

项目github地址:bearhanQ/WPFFramework: Share some experience (github.com)

QQ技术交流群:332035933;

欢迎进群讨论问题,不论是winform,还是wpf,还是.net core的,还有很多萌妹.

WPF 保姆级教程怎么实现一个树形菜单的更多相关文章

  1. 保姆级教程——Ubuntu16.04 Server下深度学习环境搭建:安装CUDA8.0,cuDNN6.0,Bazel0.5.4,源码编译安装TensorFlow1.4.0(GPU版)

    写在前面 本文叙述了在Ubuntu16.04 Server下安装CUDA8.0,cuDNN6.0以及源码编译安装TensorFlow1.4.0(GPU版)的亲身经历,包括遇到的问题及解决办法,也有一些 ...

  2. 自建本地服务器,自建Web服务器——保姆级教程!

    搭建本地服务器,Web服务器--保姆级教程! 本文首发于https://blog.chens.life/How-to-build-your-own-server.html. 先上图!大致思路就是如此. ...

  3. Eclipse for C/C++ 开发环境部署保姆级教程

    Eclipse for C/C++ 开发环境部署保姆级教程 工欲善其事,必先利其器. 对开发人员来说,顺手的开发工具必定事半功倍.自学编程的小白不知道该选择那个开发工具,Eclipse作为一个功能强大 ...

  4. 强大博客搭建全过程(1)-hexo博客搭建保姆级教程

    1. 前言 本人本来使用国内的开源项目solo搭建了博客,但感觉1核CPU2G内存的服务器,还是稍微有点重,包括服务器内还搭建了数据库.如果自己开发然后搭建,耗费时间又比较多,于是乎开始寻找轻量型的博 ...

  5. RocketMQ保姆级教程

    大家好,我是三友~~ 上周花了一点时间从头到尾.从无到有地搭建了一套RocketMQ的环境,觉得还挺easy的,所以就写篇文章分享给大家. 整篇文章可以大致分为三个部分,第一部分属于一些核心概念和工作 ...

  6. 上一个树形菜单的改进,增添了数据绑定功能而非仅仅的jq特效

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...

  7. 【良心保姆级教程】java手把手教你用swing写一个学生的增删改查模块

    很多刚入门的同学,不清楚如何用java.swing去开发出一个系统? 不清楚如何使用java代码去操作数据库进行增删改查一些列操作,不清楚java代码和数据库(mysql.sqlserver)之间怎么 ...

  8. [保姆级教程] 如何在 Linux Kernel (V5.17.7) 中添加一个系统调用(System call)

    最近在学习 <linux Kernel Development>,本书用的linux kernel 是v2.6 版本的.看完"系统调用"一节后,想尝试添加一个系统调用, ...

  9. 保姆级教程!手把手教你使用Longhorn管理云原生分布式SQL数据库!

    作者简介 Jimmy Guerrero,在开发者关系团队和开源社区拥有20多年的经验.他目前领导YugabyteDB的社区和市场团队. 本文来自Rancher Labs Longhorn是Kubern ...

  10. 保姆级教程,如何发现 GitHub 上的优质项目?

    先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有一线大厂整理的面试题,以及我的系列文章. ...

随机推荐

  1. RK3588开发笔记(四):基于定制的RK3588一体主板升级镜像

    前言   方案商定制的主板,加入了360°环视算法功能,涉及到了一些库的添加,重新制作了依赖库的镜像,镜像更新的原来的板子上.   定制的板子    升级接口type-c   设计接口是type-c, ...

  2. Redis巡检检查 redis-check-aof

    一.AOF1.AOF  是什么以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,R ...

  3. UE 实现鼠标点选模型

    楔子 在孪生的场景中,点击三维对象是常用的操作.比如点击模型显示相关属性和图片,点击摄像头模型播放视频,点击楼宇展开楼层等等. 因此点选模型是属于数字孪生最必要的基础能力. 准备知识 UE蓝图介绍 本 ...

  4. 2023 NOIP 游记

    \(\text{Day -INF}\) 提高 \(135\) 卡线进 \(\text{NOIP}\). 集训两天成绩:\(50 \to 135\). \(\text{Day 1}\) 开赛 \(13\ ...

  5. SQL Server AdventureWorks示例数据库

    SQL Server AdventureWorks2008R2 数据字典 AdventureWorks2008R2示例数据库下载 AdventureWorks2008R2数据字典(官网) Addres ...

  6. 在Ubuntu 18.04 Desktop图形中配置静态和动态IP

    在Ubuntu 18.04 图形界面中配置静态和动态IP 设置静态ip 设置为dhcp动态获取ip

  7. “Vanilla” 在计算机科学和技术领域中的专业翻译

    "Vanilla" 在计算机科学和技术领域中通常指的是某个系统或软件的"原始"或"基础"版本,即没有任何修改.扩展或定制的版本.它可以翻译为 ...

  8. 【转载】 机器学习数据可视化 (t-SNE 使用指南)—— Why You Are Using t-SNE Wrong

    原文地址: https://towardsdatascience.com/why-you-are-using-t-sne-wrong-502412aab0c0 ==================== ...

  9. 在python中numpy.sum的性能真的好吗

    首先我们应该知道np.sum是用C语言写的矢量计算,应用场景为规模较大的numpy数组求和.本文要说的就是numpy.sum是不是对规模较小的numpy数组求和也同样会有不错的性能? 代码: impo ...

  10. 多线程之深入理解park与unpark

    1.背景 面试官问,如何暂停一个线程勒..... 说说你对park的理解....... 2.代码 package com.ldp.demo01; import com.common.MyThreadU ...