在UI界面中,树形视图是比较常用的表示层级结构的方式,WPF中提供了TreeView控件。对于TreeView控件的基本使用已经有很多文章。大都是介绍如何在XAML中使用硬编码的固定信息填充Treeview控件,或者是后台代码递归遍历数据源,动态创建TreeView。这里我想介绍一下如何只通过XAML标记,不用一行后台代码遍历数据实现TreeView。

技术要点与实现

本文的技术关键点是层级式数据模板HierarchicalDataTemplateHierarchicalDataTemplate是一个特殊的DataTemplate,它能够包装第二层模板。通过ItemsSource属性查找下一层级的数据集合,并将它提供给第二层模板。这样描述可能有点晦涩。接下来举例进行描述。

首先假设一个应用场景。用树形结构展现一个地区所有的学校->年级->班级->学生。首先定义几个Model

public class School : ObservableObject
{
private bool _isOpen;
/// <summary>
/// 获取或设置是否展开
/// </summary>
[System.Xml.Serialization.XmlIgnore]
public bool IsOpen { get { return _isOpen; } set { Set(ref _isOpen, value); } } private bool _isSelected;
/// <summary>
/// 获取或设置是否被选中
/// </summary>
[System.Xml.Serialization.XmlIgnore]
public bool IsSelected { get { return _isSelected; } set { Set(ref _isSelected, value); } } public string SchoolID { get; set; }
public string SchoolName { get; set; }
public ObservableCollection<Grade> listGrade { get; set; }=new ObservableCollection<Grade>() { };
} public class Grade : ObservableObject
{
private bool _isOpen;
[System.Xml.Serialization.XmlIgnore]
public bool IsOpen { get { return _isOpen; } set { Set(ref _isOpen, value); } } private bool _isSelected;
[System.Xml.Serialization.XmlIgnore]
public bool IsSelected { get { return _isSelected; } set { Set(ref _isSelected, value); } } public string GradeID { get; set; }
public string GradeName { get; set; }
public ObservableCollection<ClassInfo> ListClass { get; set; }=new ObservableCollection<ClassInfo>() { };
} public class ClassInfo : ObservableObject
{
private bool _isOpen;
[System.Xml.Serialization.XmlIgnore]
public bool IsOpen { get { return _isOpen; } set { Set(ref _isOpen, value); } } private bool _isSelected;
[System.Xml.Serialization.XmlIgnore]
public bool IsSelected { get { return _isSelected; } set { Set(ref _isSelected, value); } } public string ClassID { get; set; }
public string ClassName { get; set; }
public ObservableCollection<Student> Students { get; set; }= new ObservableCollection<Student>() { }; } public class Student : ObservableObject
{
public string Id { get; set; }
public string Name { get; set; }
}

接下来根据定义好的Model定义层级式数据模板HierarchicalDataTemplate

<HierarchicalDataTemplate DataType="{x:Type local:School}" ItemsSource="{Binding Path=listGrade}">
<TextBlock Text="{Binding Path=SchoolName}" />
</HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="{x:Type local:Grade}" ItemsSource="{Binding Path=ListClass}">
<TextBlock Text="{Binding Path=GradeName}" />
</HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="{x:Type local:ClassInfo}" ItemsSource="{Binding Path=Students}">
<TextBlock Text="{Binding Path=ClassName}" />
</HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="{x:Type local:Student}">
<CheckBox Command="{Binding SelectChangeCommand, ElementName=self}" CommandParameter="{Binding}" IsChecked="{Binding IsSelected}">
<TextBlock Text="{Binding Path=Name}" />
</CheckBox>
</HierarchicalDataTemplate>

其中最外层数据类型是School,它的下一层数据集合是ObservableCollection<Grade> listGrade,因此HierarchicalDataTemplate中的ItemsSource赋值为listGrade,这里我们再属性控件中只显示学校的名称,因此数据模板只是包含绑定了学校名称SchoolNameTextBlock,如果需要显示其他信息(比如学校年级数量或者学校图标),只需增加相应XAML元素即可。紧接着按照这个方式定义好数据类型Grade,ClassInfo,Student的层级式数据模板即可。

定义好了数据模型和相应的层级式数据模板HierarchicalDataTemplate后,就可以直接把数据元绑定到TreeView上了。假设要绑定的数据源实例是ObservableCollection<School> schools。只需如下调用即可。

<TreeView MaxHeight="480"
ItemsSource="{Binding schools}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling" />

这样使用TreeView是不是特别方便简洁。不用为了展示树形结构,特地定义一个递归类型的数据结构,UI展示全部交给XAML就行。JSON数据反序列化后直接绑定即可(XML或者DateSet也是类似的方法)。避免了递归遍历数据源的操作,也不用考虑递归带来的性能问题。

性能

前边提到不用考虑递归带来的性能问题。那本文介绍的方法对于大量数据的情况下性能到底怎样呢?接下来做一个测试,模拟100W的数据量,具体为240个学校,每个学校3个年级,每个年级20个班,每个班70个学生,总共数据量是240x3x20x70=1008000个。以下是测试结果:

从图中可以看到模拟100w数据耗时1.5s,内存增加了160M左右,数据渲染到界面不到1s,内存增加20M左右。结果还是令人满意的。这是因为TreeView支持开启虚拟化(默认是关闭的,设置 VirtualizingPanel.IsVirtualizing="True"开启虚拟化),渲染界面是不会一次把所有UI元素全部创建好,而是根据屏幕上可见区域计算需要渲染的元素个数,创建少量的UI元素,从而减少内存和CPU资源的使用。例如本例中又100w条数据,可见区能显示20条,TreeView只创建了41个UI元素。为什么不是创建20个呢?这是由于为了确保良好的滚动性能,实际会多创建一些UI元素。

TreeView 默认关闭虚拟化,是因为早期的WPF发布版本中的VirtualizingStackPanel不支持层次化数据,虽然现在已支持,但是TreeView默认关闭虚拟化确保兼容性。

WPF中非递归(无后台代码)动态实现TreeView的更多相关文章

  1. asp.net后台代码动态添加JS文件和css文件的引用

    首先添加命名空间 using System.Web.UI.HtmlControls; 代码动态添加css文件的引用 HtmlGenericControl myCss = new HtmlGeneric ...

  2. WPF中使用后台代码来控制TreeView的选择项(SelectedItem)以及展开节点操作

    首先为TreeView控件制作一个Style: <Style x:Key="LibraryTreeViewItemStyle" TargetType="{x:Typ ...

  3. 在后台代码中动态生成pivot项并设置EventTrigger和Action的绑定

    最近在做今日头条WP的过程中,遇到需要动态生成Pivot项的问题.第一个版本是把几个频道写死在xaml里了,事件绑定也写在xaml里,每个频道绑定一个ObservableCollection<A ...

  4. WPF内嵌代码和后台代码简单混合使用

    下面实例展示了WPF内嵌代码和后台代码混合使用,一个简单基础的实例: xaml文件: <Window x:Class="WPF内嵌代码和后台代码混合使用.MainWindow" ...

  5. WPF MVVM 架构 Step By Step(4)(添加bindings - 完全去掉后台代码)

    之前的改进已经挺棒的,但是我们现在知道了后台代码的问题,那是否可能把后台代码全部去除呢?这时候就该WPF binding 和 commands 来做的事情了. WPF就是以超吊的binding,com ...

  6. WPF中Style文件的引用——使用xaml代码或者C#代码动态加载

    原文:WPF中Style文件的引用--使用xaml代码或者C#代码动态加载 WPF中控件拥有很多依赖属性(Dependency Property),我们可以通过编写自定义Style文件来控制控件的外观 ...

  7. How do I duplicate a resource reference in code behind in WPF?如何在WPF后台代码中中复制引用的资源?

    原文 https://stackoverflow.com/questions/28240528/how-do-i-duplicate-a-resource-reference-in-code-behi ...

  8. WPF后台代码实现TextBlock滚动条

    方法一: 常规的WPF操作: <ScrollViewer Width=" VerticalScrollBarVisibility="Auto" Horizontal ...

  9. WPF 后台代码做 TranslateTransform 的动画

    本文告诉大家,在后台代码,对 TranslateTransform 做动画的方法 今天小伙伴问我一个问题,说为什么相同的代码,如果设置到按钮上,是可以让按钮的某个属性变更,但是如果设置给 Transl ...

  10. WPF MVVM 架构 Step By Step(3)(把后台代码移到一个类中)

    我觉得大部分开发者应该已经知道怎么去解决这个问题.一般都是把后台代码(GLUE code)移动到一个类库.这个类库用来代表UI的属性和行为.任何代码当被移到一个类库中时都可以被编译成一个DLL,然后可 ...

随机推荐

  1. 2023-02-15:商场中有一展柜A,其大小固定,现已被不同的商品摆满, 商家提供了一些新商品B,需要对A中的部分商品进行更新替换, B中的商品可以自由使用,也就是可以用B中的任何商品替换A中的任何

    2023-02-15:商场中有一展柜A,其大小固定,现已被不同的商品摆满, 商家提供了一些新商品B,需要对A中的部分商品进行更新替换, B中的商品可以自由使用,也就是可以用B中的任何商品替换A中的任何 ...

  2. 数据分析缺失值处理(Missing Values)——删除法、填充法、插值法

    缺失值指数据集中某些变量的值有缺少的情况,缺失值也被称为NA(not available)值.在pandas里使用浮点值NaN(Not a Number)表示浮点数和非浮点数中的缺失值,用NaT表示时 ...

  3. 代码随想录算法训练营Day35 贪心算法

    代码随想录算法训练营 代码随想录算法训练营Day35 贪心算法| 860.柠檬水找零 406.根据身高重建队列 452. 用最少数量的箭引爆气球 860.柠檬水找零 题目链接:860.柠檬水找零 在柠 ...

  4. 记录一些不知道哪里冒出来的 idea(可能已存在)

    2022/7/4 给定一张 \(n\) 个点的有权无向图和 \(m\) 个关键点,求其生成树满足每个点到离它最近的关键点的距离之和最小. 输出该生成树边权和. 加强一下: 给出一个 \(N\) 个点 ...

  5. Active Directory Basic

    Active Directory 是 Windows 域网络的目录服务 介绍 Active Directory 是在域内部连接的机器和服务器的集合,它们是构成 Active Directory 网络的 ...

  6. Hive执行计划之一文读懂Hive执行计划

    目录 概述 1.hive执行计划的查看 2.学会查看Hive执行计划的基本信息 3.执行计划步骤操作过程 4.explain extended 概述 Hive的执行计划描述了一个hiveSQL语句的具 ...

  7. Java猜数字,猜完一局以后,输入y继续下一次游戏,否则结束

    代码如下: public static void main(String[] args) { String x = ""; do { int random = (int) (Mat ...

  8. Linux可视化管理-webmin工具

    环境:连接工具:tabby,操作系统:centos7.6. webmin 介绍 ​ Webmin 是功能强大的基于 Web 的 Unix/linux 系统管理工具.管理员通过浏览器访问 Webmin ...

  9. 使用 nuxt3 开发简约优雅的个人 blog

    起因 很早前我就有过搭建个人博客的想法,但是我希望使用纯前端实现,这样就不需要付出额外的后端维护成本,维护成本又低,而且更加安全.网上也有很多博客框架但是也不符合我的需求,所以我使用了nuxt3 + ...

  10. ShardingSphere5入门到实战

    ShardingSphere5入门到实战 第01章 高性能架构模式 互联网业务兴起之后,海量用户加上海量数据的特点,单个数据库服务器已经难以满足业务需要,必须考虑数据库集群的方式来提升性能.高性能数据 ...