在开发中,经常会以层次结构来组织数据,最典型的例子莫过于树视图。本文讲述Silverlight中的层次数据的递归绑定,采用TreeView作为示例控件。
在本文中,以“学校——学院——专业”作为数据层次示例,由此构建一下类型:
{
public string SchoolName { get ; set ; }
public ObservableCollection < Academy > Academys { get ; set ; }
}
public class Academy
{
public string AcademyName { get ; set ; }
public ObservableCollection < Subject > Subjects { get ; set ; }
}
public class Subject
{
public string SubjectName { get ; set ; }
}
现在希望在一个ItemsControl控件中,以层次的方式显示这些数据,正如之前所说,使用TreeView控件,毕竟它是最典型的层次数据控件。目标是只使用一句数据绑定代码,就能进行完整的树结构数据绑定,实现以下效果:
首先,定义这些数据类型,包括学校,学院,专业课程,代码如下:
{
public string SchoolName { get ; set ; }
public ObservableCollection < Academy > Academys { get ; set ; }
}
public class Academy
{
public string AcademyName { get ; set ; }
public ObservableCollection < Subject > Subjects { get ; set ; }
}
public class Subject
{
public string SubjectName { get ; set ; }
}
其次,要有数据源,因为这是演示文章,所以不使用数据库那些复杂的数据源,而使用编程生成的数据源,代码如下:
{
public static ObservableCollection < School > SchoolData
{
get
{
ObservableCollection < School > result = new ObservableCollection < School > ();
School school = null ;
Academy academy = null ;
Subject subject = null ;
school = new School() { SchoolName = " 广东工业大学 " , Academys = new ObservableCollection < Academy > () };
result.Add(school);
academy = new Academy() { AcademyName = " 计算机学院 " , Subjects = new ObservableCollection < Subject > () };
school.Academys.Add(academy);
subject = new Subject() { SubjectName = " 计算机科学与技术 " };
academy.Subjects.Add(subject);
subject = new Subject() { SubjectName = " 网络工程 " };
academy.Subjects.Add(subject);
subject = new Subject() { SubjectName = " 软件工程 " };
academy.Subjects.Add(subject);
academy = new Academy() { AcademyName = " 机电工程学院 " , Subjects = new ObservableCollection < Subject > () };
school.Academys.Add(academy);
subject = new Subject() { SubjectName = " 机械设计制造及其自动化 " };
academy.Subjects.Add(subject);
subject = new Subject() { SubjectName = " 车辆工程 " };
academy.Subjects.Add(subject);
subject = new Subject() { SubjectName = " 包装工程 " };
academy.Subjects.Add(subject);
subject = new Subject() { SubjectName = " 工业工程 " };
academy.Subjects.Add(subject);
subject = new Subject() { SubjectName = " 机械工程 " };
academy.Subjects.Add(subject);
academy = new Academy() { AcademyName = " 轻工化工学院 " , Subjects = new ObservableCollection < Subject > () };
school.Academys.Add(academy);
subject = new Subject() { SubjectName = " 食品科学与工程 " };
academy.Subjects.Add(subject);
subject = new Subject() { SubjectName = " 生物工程 " };
academy.Subjects.Add(subject);
subject = new Subject() { SubjectName = " 应用化学 " };
academy.Subjects.Add(subject);
subject = new Subject() { SubjectName = " 制药工程 " };
academy.Subjects.Add(subject);
return result;
}
}
}
上述代码中,构造了一个层次结构的数据源集合。通过SchoolData属性获取。
接下来,就是XAML中定义TreeView以及层级数据模板HierarchicalDataTemplate,也就是这个功能的核心部分,代码如下:
XAML:
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common ="clr-namespace:System.Windows;assembly=System.Windows.Controls"
xmlns:d ="http://schemas.microsoft.com/expression/blend/2008"
Loaded ="UserControl_Loaded" >
< UserControl.Resources >
< common:HierarchicalDataTemplate x:Key ="subjectDataTemplate" >
< TextBlock Text =" {Binding SubjectName} " FontSize ="14" />
</ common:HierarchicalDataTemplate >
< common:HierarchicalDataTemplate x:Key ="academyDataTemplate"
ItemsSource =" {Binding Subjects} "
ItemTemplate =" {StaticResource subjectDataTemplate} " >
< TextBlock Text =" {Binding AcademyName} " FontSize ="16" />
</ common:HierarchicalDataTemplate >
< common:HierarchicalDataTemplate x:Key ="schoolDataTemplate"
ItemsSource =" {Binding Academys} "
ItemTemplate =" {StaticResource academyDataTemplate} " >
< TextBlock Text =" {Binding SchoolName} " FontSize ="18" />
</ common:HierarchicalDataTemplate >
</ UserControl.Resources >
< Grid x:Name ="LayoutRoot" Background ="White" >
< controls:TreeView x:Name ="testInstance"
ItemTemplate =" {StaticResource schoolDataTemplate} " >
</ controls:TreeView >
</ Grid >
</ UserControl >
第一部分的高亮代码,是使用HierarchicalDataTemplate控件所需要的命名空间引用。
第二部分的高亮代码,定义了三个HierarchicalDataTemplate数据模板控件,定义在UserControl的资源中。(说明了这个数据模板可以定义在多种位置上。只要TreeView能引用就可以)。从上到下,第一个定义的是subjectDataTemplate,这个数据模板用于呈现专业课程数据项。第二个定义的是academyDataTemplate,同样,这个数据模板定义的是用于呈现学院数据项的,但不一样的是:
1.这个数据模板还定义了ItemsSource与ItemTemplate。它们的意义分别如下:
a. ItemsSource={Binding Subjects}
此代码的功能就是:在这个呈现学院数据的数据模板中,假如数据源(代表
学院的数据对象)存在一个Subjects属性,就把该属性返回的数据绑定到学
院数据项的所有子项当中。而之前定义的Academy类型中,的确存在Subject
s属性,因此该属性返回的所有专业课程对象,都会绑定到学院数据项的所有
子项中。
b. ItemTemplate={StaticResource subjectDataTemplate}
此代码的功能就是:学院数据项的子项使用什么数据模板来呈现子项数据,
这样就很容易理解为什么使用资源中的subjectDataTemplate数据模板,因为
该模板就是为了呈现专业课程数据而定义的。(也正因如此,所以subjectDat
aTemplate的定义必须在academyDataTemplate的定义之后,否则不能通过
XAML中的语法进行资源引用)
接下来的schoolDataTemplate定义实现的是同样的含义,请读者自己分析一下,加深理解。综上,可以看到,实现层次结构数据的连锁绑定,关键就在于HierarchicalDataTemplate的ItemsSource及ItemTemplate属性定义。
第三部分的高亮代码,定义了一个TreeView控件。该控件的ItemTemplate使用了schoolDataTemplate,也就是说,TreeView以及其他ItemsControl控件,在它们的子项数据模板中,只需要应用顶层的HierarchicalDataTemplate数据模板,而往后各个子层的数据模板是怎样,数据源绑定什么属性,都由各层的HierarchicalDataTemplate定义决定。
接下来就是对TreeView进行数据绑定的程序代码了。我把这个绑定过程写在UserControl的Loaded事件中,当然可以写在别的地方。代码如下:
{
testInstance.ItemsSource = Data.SchoolData;
}
就是这样简单的数据绑定代码,就解决对一整个层次结构的数据显示问题。由于我们使用的都是ObservableCollection<T>,因此对集合的任何修改都会马上更新到TreeView中。来到这里已经解决了各层数据异构的绑定问题。那如果各层数据是同构的怎样呢?(异构的意思指每层的数据对象是不一样的,例如之前例子的School——Academy——Subject,同构的例子有文件目录数据)。
同样首先是数据对象的定义,代码如下:
{
public string FileName { get ; set ; }
public ObservableCollection < FileInfo > ChildFiles { get ; set ; }
}
构造数据源的代码如下:
{
public static ObservableCollection < FileInfo > FileCatalog
{
get
{
ObservableCollection < FileInfo > result = new ObservableCollection < FileInfo > ();
FileInfo root = new FileInfo() { FileName = " RootFile " , ChildFiles = new ObservableCollection < FileInfo > () };
result.Add(root);
FileInfo file_i = null ;
FileInfo file_j = null ;
FileInfo file_k = null ;
for ( int i = 0 ; i < 3 ; i ++ )
{
file_i = new FileInfo() { FileName = " File_ " + i, ChildFiles = new ObservableCollection < FileInfo > () };
root.ChildFiles.Add(file_i);
for ( int j = 0 ; j < 5 ; j ++ )
{
file_j = new FileInfo() { FileName = file_i.FileName + " _ " + j, ChildFiles = new ObservableCollection < FileInfo > () };
file_i.ChildFiles.Add(file_j);
for ( int k = 0 ; k < 4 ; k ++ )
{
file_k = new FileInfo() { FileName = file_j.FileName + " _ " + k, ChildFiles = new ObservableCollection < FileInfo > () };
file_j.ChildFiles.Add(file_k);
}
}
}
return result;
}
}
}
顶层数据项的HierarchicalDataTemplate数据模板定义为:
ItemsSource =" {Binding ChildFiles} " >
< TextBlock Text =" {Binding FileName} " FontSize ="15" />
</ common:HierarchicalDataTemplate >
修改一下TreeView,使用这个数据模板,就可以实现递归数据绑定。这样,每一层的数据呈现都使用相同的数据模板。也就是说,当前HierarchicalDataTemplate不指定下一层子项的ItemTemplate时,就会递归使用自身作为子项数据模板。运行结果如下: