[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:

代码
     <controls:TreeView x:Name="testInstance"
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>

  事件处理程序代码:

代码
private void OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
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做以下替换:

  

代码
private void OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
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 。

posted @ 2010-05-09 20:07  DOF_KL  阅读(3830)  评论(0编辑  收藏  举报