TreeView表示的是层次化结构(hierarchical)数据。TreeView的每一项称为TreeViewItem。TreeViewItem既可以被定义成一个字符串,也可以是一个嵌套的Item对象集合。TreeView的类层次结构如下:
Control
ItemsControl
HeaderedItemsControl
MenuItem
ToolBar
TreeViewItem
MenuBase (abstract)
ContextMenu
Menu
StatusBar
TreeView
由上图可见,ItemsControl是TreeView的父类。ItemsControl有一个重要的属性就是Items,它是包含了一系列Item的集合。而从HeaderItemsControl继承的TreeViewItem类则多了一个属性Header,这是一个object。
树的展开
树的数据来源一般是数据文件或者是程序外的其他信息。例如我们想显示盘符和该盘符下的文件目录。示例代码如下:
对于构建一个树,最先想到的就是采用递归算法(recursive)。上面的例子就是如此。在实际运行时,我们会发现在显示窗口界面之前花费了很长的时间在查询所有的盘符。但有时候用户并不想查看所有的盘符目录。
更好的办法是,当用户点击节点前的“+”号查询其节点目录时,我们只需展开它的下一级目录。只有当节点有子Item时才会有加号,因此在构建节点时,如果节点有子节点,必须通过加号表示出来,但此时该节点的子节点不必包含进去。采用这种方法就是迟加载(lazy loading)的方式。
TreeView提供了四个事件来支持迟加载。Expanded(展开事件):当点击加号时显示子节点或者手动设置IsExpanded属性为True时发生。Collapse(折叠事件):与Expanded相反。Selected(选中事件):一个节点被选中或者IsSelected属性设置为True是发生。UnSelected(不选中事件):与Selected事件相反。这四个事件都会触发相应的方法。
通过以上方法,我们可以方便的实现树的展开。我们在初始化时首先加载各个盘符下的目录。当点击某个节点盘符时,我们显示该节点子目录,并加载该节点下每个节点的目录。
关于Selected和UnSelected
当对添加的TreeViewItem显式订阅这两个事件时,如果子节点别选中,则父节点也会被选中,直到树的根节点。选中顺序为子-父。当切换到另一个节点时,则原来的选中节点变成不选中。顺序也是子-父。然后重新选中新的子节点和父。
但是当Item派生自TreeViewItem,不订阅事件,而是重写父节点的OnSelected和OnUnselected函数时。在进行节点选择时,则不会同时选中父节点。
关于Preview。。。事件
当响应该事件时,选中的子节点和父节点都会响应。顺序为父-子。
右键点击Item时弹出菜单
完成此任务需要两个部分,Item选中和弹出右键菜单。选中,在PreviewMouseDown或PreviewMouseRightButtonDown事件中执行。 右键菜单通过ContextMenu设定。
TreeViewItem的Tag是Object,所以一般在Tag中存放定义的数据结构对象。在Header存放可辨识的图片之类的对象。由于Selected和Preview等事件的遍历关系,所我一般不在SelectedChanged和Preview等事件中构造界面。而应该放到右键菜单中实现。
在使用TabControl时,也是类似的遍历情况。当TabItem中的某个控件(如combobox)执行selected事件时,也会执行Tabcontrol的Selectedchanged事件,使用时要注意。