[Silverlight]TreeView控件——动态加载子项
操作系统的课程设计是做一个文件系统。我选择了用Silverlight做出该文件操作系统的界面,因为打算吃些在OJ和团队网站上用上该界面,一举三得。 在文件操作系统的界面很自然少不了树状文件结构,该控件第一个令我想到的效果就当然是动态加载文件结构,毫无疑问,这是很好的用户体验与软件性能的结合。效果参加下图:
点击目录前的界面:
点击目录Catalog0-2后的界面:
细心的读者可以发觉,在点击前,Catalog0-2这个目录项的前方没有那个小箭头图标,点击后的目录才有,这算是动态加载的一个证明。由于篇幅问题,不能给出完整的代码。以下说一下实现动态加载的几个途径:
1)通过Items属性动态添加子项:
TreeView中的每个子项是TreeViewItem类型(例如图中的Catalog0-0 ~ Catalog0-9)。TreeViewItem类型继承自HeaderedItemsControl(读者可先忽略这个类型的细节),而后者继承自ItemsControl,这个ItemsControl类有一个Items属性,通过该属性返回的集合,就可以添加或删除子项:
TreeViewItem sampleItem = new TreeViewItem();
sampleItem.Items.Add(new Object());
再者,TreeView也是继承自ItemsControl,后者具有一个SelectedItemChanged事件,顾名思义,当在界面上选中一个子项时,就会触发该事件(注意触发者是父项)。
因此,首个动态加载子目录的方式就是:
a. 捕捉TreeView的SelectedItemChanged事件;
b. 在事件处理代码中,调用TreeView的SelectedItem属性,从而获取被选中的
TreeViewItem;(图中的例子,选中的就是Catalog0-2这个TreeViewItem)
c. 调用选中的TreeViewItem的Items属性来添加新的子项,实现动态加载。
关键代码:(此代码并非上图中的效果代码,是针对此实现方式的代码)
树控件Xaml:
SelectedItemChanged="OnSelectedItemChanged">
<controls:TreeViewItem Header="Catalog0-0" />
<controls:TreeViewItem Header="Catalog0-1" />
<controls:TreeViewItem Header="Catalog0-2" />
<controls:TreeViewItem Header="Catalog0-3" />
<controls:TreeViewItem Header="Catalog0-4" />
<controls:TreeViewItem Header="Catalog0-5" />
<controls:TreeViewItem Header="Catalog0-6" />
<controls:TreeViewItem Header="Catalog0-7" />
<controls:TreeViewItem Header="Catalog0-8" />
<controls:TreeViewItem Header="Catalog0-9" />
</controls:TreeView>
事件处理程序代码:
{
TreeView source = sender as TreeView;
TreeViewItem selectedItem = source.SelectedItem as TreeViewItem;
for (int i = 0; i < 5; i++)
{
selectedItem.Items.Add(new TreeViewItem() { Header = selectedItem.Header + "-" + i });
}
}
2)通过ItemsSource绑定子项数据源:
前面讲到的ItemsControl还有一个很重要的属性——ItemsSource。当将一个继承自IEnumerable或IEnumerable<T>的对象(即一个可以枚举的集合)设置到该属性时,ItemsControl就会自动将可枚举集合中的所有对象绑定为自己的子项。上面的事件处理程序代码可以用ItemsSource做以下替换:
{
TreeView source = sender as TreeView;
TreeViewItem selectedItem = source.SelectedItem as TreeViewItem;
IList<TreeViewItem> newItems = new List<TreeViewItem>();
for (int i = 0; i < 5; i++)
{
newItems.Add(new TreeViewItem() { Header = selectedItem.Header + "-" + i });
}
selectedItem.ItemsSource = newItems;
}
采用这种方式,可以享受到很多.net数据绑定的功能。但要注意的就是,一旦使用了ItemsScource来绑定子项数据源,Items属性返回的子项集合将是一个只读集合,任何对该集合调用的Add、Remove等修改集合内容的操作,都会触发异常,这意味着两种方式是互斥的。
上述代码中,我只对根TreeView控件注册了SelectedItemChanged事件的处理程序,但被添加到子项中的TreeViewItem也会自动使用该事件处理程序,子项的子项也会,如此递归。动手测试上述代码的读者会发觉效果很有趣。
当使用ObservableCollection<T>类作为数据源绑定时,效果更佳,因为所有对数据源集合的操作,都会自动更新到树控件的视图上。而一般的集合,在绑定之后,对原集合的操作是不会自动被更新到树控件的视图上。手动更新的方式是重新设置ItemsScource属性,尽管还是使用同一个数据源集合,但MSDN上已经有文档说明,这种方式比起使用ObservableCollection<T>的方式时间要慢几十倍。文章链接为:http://msdn.microsoft.com/zh-cn/library/bb613546.aspx 。