写点什么

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

  • 2024-09-09
    福建
  • 本文字数:4240 字

    阅读完需:约 14 分钟

先看一下效果吧:


    

我们直接通过改造一下原版的 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,这是一个转义字符,就这样我们就能添加如何自己喜欢的图标了。


文章转载自:BearHan

原文链接:https://www.cnblogs.com/lvpp13/p/18400310

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
WPF 保姆级教程怎么实现一个树形菜单_大数据_快乐非自愿限量之名_InfoQ写作社区