ItemsControl
有时需要呈现一组逻辑上平级的控件,他们可以是一个列表,也可以是一个网格;可以横向排列,也可以纵向排列;数量可以固定,也可以按需加载;普通控件的组合无能为力。所以,我们需要新的工具。ItemsControl
应运而生。
ItemsControl自身是个控件,所以它具有继承自控件的布局属性(Margin,Padding,Background等);WPF作为一个数据驱动界面的框架,它要负责根据数据生成每个Item;多个Item之间需要某种排列逻辑,这也是ItmesControl的任务;在Item可被选择的时候,选中的和未选中的要在视觉上做区分。这种视觉区分最好不需要开发者自己操心。这个光荣的任务又落在了ItemsControl的头上;独立于数据,Item自身需要排序,过滤;逻辑上能够归为一类的Item有时要放在一起,这就又牵扯到分组的了。
ItemsControl职责的列表能列很长,况且这还没说数据虚拟化和界面虚拟化呢。秉持着面向对象设计的原则,ItemsControl这里肯定牵扯到了很多的类。
事实也是如此。为了实现继承自控件的属性,ItemsControl需要把自己放在一个Panel里。为了实现数据驱动界面,ItemsControl借助了ItemContainerGenerator,一个由数据生成UI界面的类;为了解决Item之间的排布方式,引入了ItemsPanel;为了实现不需开发者操心的选中/未选状态的视觉区分,在Item外面套了一层容器(ItemContainer)。这么来看,ItemsControl的可视化树会长成这样就不奇怪了:
ItemsControl其实并不常用,日常使用接触最多的是它的子类:
- ListView
- ListBox
- ComboBox
- TreeView
相比聪明的你已经从这个系列的名字里猜出来了,我们要研究的是TreeView
TreeView,TreeViewItem与HeaderedItemsControl
TreeView是个很特殊的ItemsControl。列表也好,网格也好,其他的ItemsControl所能展示的都是同一等级的Item,而TreeView却可以展示多级。这其中的奥秘就藏在ItemContainer里。
每个ItemControl的子类都有其对应的ItemContainer,ListView对应ListViewItem,ComboBox对应ComboBoxItem,聪明的你,快来想想TreeView对应的是什么😋
现在思考一个问题,把ItemsControl的可视化树中的一个哪一个节点替换掉,会让ItemsControl出现多级的结构?//提示:看本节名字
揭晓答案。一个很自然的想法是,如果TreeView的ItemContainer,即TreeViewItem,自己也是一个ItemsControl的话,那就可以出现两级结构了。进一步想下去,TreeViewItem如果是个ItemsControl的话,它必然要有ItemContainer。如果这个ItemContainer恰好是TreeViewItem呢?要是这样的话,TreeViewItem下面可以再有任意多个TreeViewItem。递归的想下去,这样TreeViewItem就可以有无数级了。
其实,TreeView的实现跟我们猜想是基本一致的。只是我们的猜想中,TreeViewItem里没有位置用来呈现当前的Item,这就引入了另一个类,HeaderedItemsControl。它继承自ItemsControl,添加了Header属性。这个Header属性就可以用来呈现我们的Item。
那么TreeView的可视化树是这样的
其中每一级的TreeViewItem都可以展开为这样的结构:
在WPF这个数据驱动界面的UI框架里,想要呈现递归的UI界面,最自然的方法就是把数据也变得递归化。即,数据也要变成树状结构。(严格来说应该是数据结构中森林的概念。TreeView是森林,TreeViewItem则是一棵树。对应地,数据也要变成森林的结构。即,树状的集合)
既然UI结构已经递归化,原来指明单层Items如何呈现的ItemTemplate已经不够用了。HierarchicalItemTemplate
闪亮登场。
最重要的属性,ItemsSource
是干什么的,我们卖个关子。
FluentTreeView
终于要讲讲什么是FluentTreeView了。作为一个十多岁的UI框架,WPF已经进入了维护期。新出生的UWP功能尚且不全,导致很多应用只得使用WPF。相比UWP,WPF的逊色的地方就在于外观。每次功能更新,UWP默认的外观就向Windows的未来的样子,Fluent design靠近一步。为了让WPF也能变得更现代一些,本系列旨在将WPF控件Fluent design化。
空口无凭,来看看我们的目标
- 左侧有当前主题色(Accent color)的选中高亮指示
- 鼠标高亮撑满整个控件宽度
在实现FluentTreeView的时候,经常ItemsControl和TreeView的相关知识。所以看不懂后面的时候,尽管回来翻看第一篇。那么我们下篇见。