首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

WPF 保姆级教程怎么实现一个树形菜单

编程知识
2024年09月06日 14:22

先看一下效果吧:

    

我们直接通过改造一下原版的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>

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

这个笑脸是怎么来的了

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

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

 

 

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

QQ技术交流群:332035933;

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

 

From:https://www.cnblogs.com/lvpp13/p/18400310
本文地址: http://www.shuzixingkong.net/article/1792
0评论
提交 加载更多评论
其他文章 C++创建与调用dll动态链接库(MinGW64 Dev-C++)
本文使用的是dev-c++,如果涉及到VC++中不一样的操作,也会适当进行区分。 项目一:创建DLL 1、创建一个DLL类型的项目,当前命名为dlltest,并选择合适的路径进行保存。 2、在生成的预设置代码中,加入如下代码 //这是头文件dll.h #ifndef _DLL_H_ #define
C++创建与调用dll动态链接库(MinGW64 Dev-C++) C++创建与调用dll动态链接库(MinGW64 Dev-C++) C++创建与调用dll动态链接库(MinGW64 Dev-C++)
R-Adapter:零样本模型微调新突破,提升鲁棒性与泛化能力 | ECCV 2024
大规模图像-文本预训练模型实现了零样本分类,并在不同数据分布下提供了一致的准确性。然而,这些模型在下游任务中通常需要微调优化,这会降低对于超出分布范围的数据的泛化能力,并需要大量的计算资源。论文提出新颖的Robust Adapter(R-Adapter),可以在微调零样本模型用于下游任务的同时解决这
R-Adapter:零样本模型微调新突破,提升鲁棒性与泛化能力 | ECCV 2024 R-Adapter:零样本模型微调新突破,提升鲁棒性与泛化能力 | ECCV 2024 R-Adapter:零样本模型微调新突破,提升鲁棒性与泛化能力 | ECCV 2024
感知机模型
一、概述 感知机模型(Perceptron Model)也叫做神经元模型,设计灵感即来自于生物神经元的运行机制,依次完成信息接收、处理、输出的过程。当前大放异彩的各种人工神经网络模型即由一个个人工神经元构成,因此,本文介绍的感知机模型(神经元模型)就是各种神经网络模型的基本单元。 二、模型原理 模型
感知机模型 感知机模型 感知机模型
JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的区别
JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的区别
JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的区别 JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的区别 JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的区别
消费降级,我的订阅服务瘦身
前言 前几天看到一篇文章,《消费降级,我的订阅服务瘦身》。 自己平时花钱有点大手大脚的,也没有统计个每个月固定的开销,现在正好趁这个机会记录一下。现在挣钱不容易,看下哪些开销可以进行降级。 腾讯云 - 服务器 分类 周期及成本 需要程度 网络服务 510元/年 需要/续订 这个服务器是平时自建博客,
消费降级,我的订阅服务瘦身 消费降级,我的订阅服务瘦身 消费降级,我的订阅服务瘦身
Git使用经验总结6-删除远端历史记录
删除远端的历史记录但是不影响最新的仓库内容是笔者一直想实现的功能,有两个很不错的用处: 有的历史提交不慎包含了比较敏感的信息,提交的时候没注意,过了一段时间才发现。这个时候已经有了很多新的历史提交,无法再回退了。 有时候会拿Git仓库存储代码文件以外的内容,比如美术资源、依赖库等等。这时除了少数提交
.NET 压缩/解压文件
本文为大家介绍下.NET解压/压缩zip文件。虽然解压缩不是啥核心技术,但压缩性能以及进度处理还是需要关注下,针对使用较多的zip开源组件验证,给大家提供个技术选型参考 之前在《.NET WebSocket高并发通信阻塞问题 - 唐宋元明清2188 - 博客园 (cnblogs.com)》讲过,团队
.NET 压缩/解压文件 .NET 压缩/解压文件 .NET 压缩/解压文件
受 LabelImg 启发的基于 web 的图像标注工具,基于 Vue 框架
受 LabelImg 启发的基于 web 的图像标注工具,基于 Vue 框架 哟,网友们好,年更鸽子终于想起了他的博客园密码。如标题所述,今天给大家带来的是一个基于 vue2 的图像标注工具。至于它诞生的契机呢,应该是我导 pass 掉了我的提议(让甲方使用 LabelImg 进行数据标注),说是要
受 LabelImg 启发的基于 web 的图像标注工具,基于 Vue 框架 受 LabelImg 启发的基于 web 的图像标注工具,基于 Vue 框架 受 LabelImg 启发的基于 web 的图像标注工具,基于 Vue 框架