在开发中,经常会以层次结构来组织数据,最典型的例子莫过于树视图。本文讲述Silverlight中的层次数据的递归绑定,采用TreeView作为示例控件。
在本文中,以“学校——学院——专业”作为数据层次示例,由此构建一下类型:
代码
public
class
School
{
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控件,毕竟它是最典型的层次数据控件。目标是只使用一句数据绑定代码,就能进行完整的树结构数据绑定,实现以下效果:
![[Silverlight]HierarchicalDataTemplate层次数据](http://img.e-com-net.com/image/product/32c263dbc709466c927fa8625899c7b2.png)
首先,定义这些数据类型,包括学校,学院,专业课程,代码如下:
代码
public
class
School
{
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
class
Data
{
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:
代码
<
UserControl
xmlns:controls
="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
x:Class
="SilverlightApplication4.MainPage"
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事件中,当然可以写在别的地方。代码如下:
private
void
UserControl_Loaded(
object
sender, RoutedEventArgs e)
{
testInstance.ItemsSource
=
Data.SchoolData;
}
就是这样简单的数据绑定代码,就解决对一整个层次结构的数据显示问题。由于我们使用的都是ObservableCollection<T>,因此对集合的任何修改都会马上更新到TreeView中。来到这里已经解决了各层数据异构的绑定问题。那如果各层数据是同构的怎样呢?(异构的意思指每层的数据对象是不一样的,例如之前例子的School——Academy——Subject,同构的例子有文件目录数据)。
同样首先是数据对象的定义,代码如下:
public
class
FileInfo
{
public
string
FileName {
get
;
set
; }
public
ObservableCollection
<
FileInfo
>
ChildFiles {
get
;
set
; }
}
构造数据源的代码如下:
代码
public
static
class
FileData
{
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数据模板定义为:
代码
<
common:HierarchicalDataTemplate
x:Key
="fileDataTemplate"
ItemsSource
="
{Binding ChildFiles}
"
>
<
TextBlock
Text
="
{Binding FileName}
"
FontSize
="15"
/>
</
common:HierarchicalDataTemplate
>
修改一下TreeView,使用这个数据模板,就可以实现递归数据绑定。这样,每一层的数据呈现都使用相同的数据模板。也就是说,当前HierarchicalDataTemplate不指定下一层子项的ItemTemplate时,就会递归使用自身作为子项数据模板。运行结果如下:
![[Silverlight]HierarchicalDataTemplate层次数据](http://img.e-com-net.com/image/product/8c2b903a7ae74f24a65553de815f1958.png)