WPF中非递归(无后台代码)动态实现TreeView
在UI界面中,树形视图是比较常用的表示层级结构的方式,WPF中提供了TreeView控件。对于TreeView控件的基本使用已经有很多文章。大都是介绍如何在XAML中使用硬编码的固定信息填充Treeview控件,或者是后台代码递归遍历数据源,动态创建TreeView。这里我想介绍一下如何只通过XAML标记,不用一行后台代码遍历数据实现TreeView。
技术要点与实现
本文的技术关键点是层级式数据模板HierarchicalDataTemplate。HierarchicalDataTemplate是一个特殊的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,这里我们再属性控件中只显示学校的名称,因此数据模板只是包含绑定了学校名称SchoolName的TextBlock,如果需要显示其他信息(比如学校年级数量或者学校图标),只需增加相应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的更多相关文章
- asp.net后台代码动态添加JS文件和css文件的引用
首先添加命名空间 using System.Web.UI.HtmlControls; 代码动态添加css文件的引用 HtmlGenericControl myCss = new HtmlGeneric ...
- WPF中使用后台代码来控制TreeView的选择项(SelectedItem)以及展开节点操作
首先为TreeView控件制作一个Style: <Style x:Key="LibraryTreeViewItemStyle" TargetType="{x:Typ ...
- 在后台代码中动态生成pivot项并设置EventTrigger和Action的绑定
最近在做今日头条WP的过程中,遇到需要动态生成Pivot项的问题.第一个版本是把几个频道写死在xaml里了,事件绑定也写在xaml里,每个频道绑定一个ObservableCollection<A ...
- WPF内嵌代码和后台代码简单混合使用
下面实例展示了WPF内嵌代码和后台代码混合使用,一个简单基础的实例: xaml文件: <Window x:Class="WPF内嵌代码和后台代码混合使用.MainWindow" ...
- WPF MVVM 架构 Step By Step(4)(添加bindings - 完全去掉后台代码)
之前的改进已经挺棒的,但是我们现在知道了后台代码的问题,那是否可能把后台代码全部去除呢?这时候就该WPF binding 和 commands 来做的事情了. WPF就是以超吊的binding,com ...
- WPF中Style文件的引用——使用xaml代码或者C#代码动态加载
原文:WPF中Style文件的引用--使用xaml代码或者C#代码动态加载 WPF中控件拥有很多依赖属性(Dependency Property),我们可以通过编写自定义Style文件来控制控件的外观 ...
- 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 ...
- WPF后台代码实现TextBlock滚动条
方法一: 常规的WPF操作: <ScrollViewer Width=" VerticalScrollBarVisibility="Auto" Horizontal ...
- WPF 后台代码做 TranslateTransform 的动画
本文告诉大家,在后台代码,对 TranslateTransform 做动画的方法 今天小伙伴问我一个问题,说为什么相同的代码,如果设置到按钮上,是可以让按钮的某个属性变更,但是如果设置给 Transl ...
- WPF MVVM 架构 Step By Step(3)(把后台代码移到一个类中)
我觉得大部分开发者应该已经知道怎么去解决这个问题.一般都是把后台代码(GLUE code)移动到一个类库.这个类库用来代表UI的属性和行为.任何代码当被移到一个类库中时都可以被编译成一个DLL,然后可 ...
随机推荐
- 2022-12-13:游戏玩法分析 I。写一条 SQL 查询语句获取每位玩家 第一次登陆平台的日期。 +-----------+-------------+ | player_id | first_l
2022-12-13:游戏玩法分析 I.写一条 SQL 查询语句获取每位玩家 第一次登陆平台的日期. ±----------±------------+ | player_id | first_log ...
- 2020-12-29:mysql中,innodb表里,某一条数据删除了之后,这条数据会被真实的擦掉吗,还是删除了关系?
福哥答案2020-12-29:[答案来自此链接,答案相当详细:](https://www.zhihu.com/question/436957843)面试的时候受 <MySQL技术内幕 InnoD ...
- 2022-03-24:你被请来给一个要举办高尔夫比赛的树林砍树,树林由一个 m x n 的矩阵表示, 在这个矩阵中: 0 表示障碍,无法触碰 1 表示地面,可以行走 比 1 大的数 表示有树的单元格,
2022-03-24:你被请来给一个要举办高尔夫比赛的树林砍树,树林由一个 m x n 的矩阵表示, 在这个矩阵中: 0 表示障碍,无法触碰 1 表示地面,可以行走 比 1 大的数 表示有树的单元格, ...
- 【Python】爬虫下载视频
Python爬虫下载视频 前言 这两天我一时兴起想学习 PS ,于是去我的软件宝库中翻出陈年已久的 PhotoshopCS6 安装,结果发现很真流畅诶! 然后去搜索学习视频,网上的视频大多浮躁,收费, ...
- Bio+IT 生信科技爱好者知识库
欢迎来到 Bio+IT 生信科技爱好者知识库! 关于我们 "BioIT爱好者" 是一个专注于生物信息学和 IT 互联网技术的知识库平台,是一个集合了 Bio+IT 相关的数据分析. ...
- 用 Python 帮运营妹纸快速搞定 Excel 文档
Microsoft Office 被广泛用于商务和运营分析中, 其中 Excel 尤其受欢迎.Excel 可以用于存储表格数据.创建报告.图形趋势等.在深入研究用 Python 处理 Excel 文档 ...
- Java中读取用户输入的是谁?Scanner类
前言 我们在初学 Java 编程的时候,总是感觉很枯燥乏味,想着做点可以交互的小系统,可以让用户自由输入,系统可以接收做出反映.这就要介绍一下 Java 中的 Scanner 类了. 一.Scanne ...
- .NET周报 【6月第1期 2023-06-04】
专题 - NanoFramework项目案例 如果有时间,我会在周报中加入一些专题和项目案例的分享,本周就是讨论.NET NanoFramework项目案例的专题,在讨论 NanoFramework ...
- Vue3从入门到精通(二)
vue3 侦听器 在Vue3中,侦听器的使用方式与Vue2相同,可以使用watch选项或$watch方法来创建侦听器.不同之处在于,Vue3中取消了immediate选项,同时提供了新的选项和API. ...
- C++面试八股文:什么是空指针/野指针/悬垂指针?
某日二师兄参加XXX科技公司的C++工程师开发岗位第30面: 面试官:什么是空指针? 二师兄:一般我们将等于0/NULL/nullptr的指针称为空指针.空指针不能被解引用,但是可以对空指针取地址. ...