[原创]FineUI秘密花园(二十四) — 树控件之数据绑定
上一篇文章我们介绍了树控件的基本用法,不过都是通过标签来声明树控件的结构,本章我们会详细讲解如何在后台绑定树控件。
绑定到XmlDocument
下面通过一个简单的例子来看如何将XmlDocument和树控件绑定,页面的标签结构:
1: <ext:Tree ID="Tree1" Width="500px" EnableArrows="false" EnableLines="false" ShowHeader="true"
2: Title="树控件(绑定到 XmlDocument)" runat="server">
3: </ext:Tree>
这里有两个属性需要注意:
- EnableArrows:是够启用箭头折叠标示,否则是默认的加减折叠标示。
- EnableLines:是否启用折叠标示之间的连接虚线。
来看下后台的初始化代码:
1: private void LoadData()
2: {
3: string xmlPath = Server.MapPath("~/tree/databind/website.xml");
4:
5: string xmlContent = String.Empty;
6: using (StreamReader sr = new StreamReader(xmlPath))
7: {
8: xmlContent = sr.ReadToEnd();
9: }
10:
11: XmlDocument xdoc = new XmlDocument();
12: xdoc.LoadXml(xmlContent);
13:
14: Tree1.DataSource = xdoc;
15: Tree1.DataBind();
16: }
这段代码的逻辑很简单:
- 获得需要读取XML文件的服务器路径;
- 使用StreamReader来读取文件的内容;
- 创建XmlDocument实例,并加载XML文件内容;
- 设置树控件的DataSource为此实例,并调用DataBind执行数据绑定。
最后来看下XML文件的内容和最终的效果截图:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <Tree>
3: <TreeNode Text="中国" Expanded="true" NodeId="China">
4: <TreeNode Text="河南省" Expanded="true" NodeId="henan">
5: <TreeNode Text="驻马店市" NodeId="zhumadian" />
6: <TreeNode Text="漯河市" NodeId="luohe" />
7: </TreeNode>
8: // 省略其他节点...
9: </TreeNode>
10: </Tree>
绑定到XmlDataSource
绑定到XmlDataSource简化了上面的步骤,我们来看一下实现相同功能的示例:
1: <ext:Tree ID="Tree1" Width="500px" EnableArrows="true" EnableSingleExpand="true"
2: ShowHeader="true" Title="树控件(绑定到 XmlDataSource)" runat="server">
3: </ext:Tree>
4: <asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="~/tree/databind/website.xml">
5: </asp:XmlDataSource>
在来看下后台初始化代码和显示效果:
1: private void LoadData()
2: {
3: Tree1.DataSource = XmlDataSource1;
4: Tree1.DataBind();
5: }
注意:在ASPX中设置了树控件的EnableSingleExpand属性,也就是说同一级目录只能展开一个节点。
绑定到SiteMap
不知道你有没有注意到,上面两个例子XML文件中定义的节点属性和树节点的属性一模一样,如果不一样怎么办?
没关系,可以为树控件指定映射关系,用来将XML中定义的节点属性名称和树节点的属性进行对应,SiteMap就是一个典型例子。
来看一下SiteMap文件:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
3: <siteMapNode title="中国" Expanded="true" NodeId="China">
4: <siteMapNode title="河南省" description="河南省省会" Expanded="true" NodeId="henan">
5: <siteMapNode title="驻马店市" NodeId="zhumadian" />
6: <siteMapNode title="漯河市" NodeId="luohe" />
7: </siteMapNode>
8: // 省略其他节点...
9: </siteMapNode>
10: </siteMap>
其中title, description, url是SiteMap节点标准的属性名称,下面我们看下ASPX标签的定义,看看如何将这些属性名映射到树节点的属性:
1: <ext:Tree ID="Tree1" Width="500px" ShowHeader="true" Title="树控件(绑定到 SiteMap)" runat="server">
2: <Mappings>
3: <ext:XmlAttributeMapping From="url" To="NavigateUrl" />
4: <ext:XmlAttributeMapping From="title" To="Text" />
5: <ext:XmlAttributeMapping From="description" To="ToolTip" />
6: </Mappings>
7: </ext:Tree>
8: <asp:XmlDataSource ID="XmlDataSource2" runat="server" DataFile="~/tree/databind/Web.sitemap">
9: </asp:XmlDataSource>
看下最终的显示效果,特别注意“河南省”节点的Tooltip:
绑定到DataTable
在实际项目中,我们可能需要从一个数据表中读出具有层次结构的数据,比如菜单表中通过ID和ParentID来定义这种结构。
下面我们通过一段代码来生成类似的DataTable结构:
1: private DataTable CreateDataTable()
2: {
3: DataTable table = new DataTable();
4: DataColumn column1 = new DataColumn("Id", typeof(string));
5: DataColumn column2 = new DataColumn("Text", typeof(String));
6: DataColumn column3 = new DataColumn("ParentId", typeof(string));
7: table.Columns.Add(column1);
8: table.Columns.Add(column2);
9: table.Columns.Add(column3);
10:
11: DataRow row = table.NewRow();
12: row[0] = "china";
13: row[1] = "中国";
14: row[2] = DBNull.Value;
15: table.Rows.Add(row);
16:
17: row = table.NewRow();
18: row[0] = "henan";
19: row[1] = "河南省";
20: row[2] = "china";
21: table.Rows.Add(row);
22:
23: row = table.NewRow();
24: row[0] = "zhumadian";
25: row[1] = "驻马店市";
26: row[2] = "henan";
27: table.Rows.Add(row);
28:
29: row = table.NewRow();
30: row[0] = "luohe";
31: row[1] = "漯河市";
32: row[2] = "henan";
33: table.Rows.Add(row);
34:
35: // 省略其他节点...
36:
37: return table;
38: }
毫无疑问,这段代码生成的层次结构如下所示:
当然,这也是我们最终要实现的效果,来看下树控件的初始化代码:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: if (!IsPostBack)
4: {
5: LoadData();
6: }
7: }
8:
9: private void LoadData()
10: {
11: DataTable table = CreateDataTable();
12:
13: DataSet ds = new DataSet();
14: ds.Tables.Add(table);
15: ds.Relations.Add("TreeRelation", ds.Tables[0].Columns["Id"], ds.Tables[0].Columns["ParentId"]);
16:
17: foreach (DataRow row in ds.Tables[0].Rows)
18: {
19: if (row.IsNull("ParentId"))
20: {
21: TreeNode node = new TreeNode();
22: node.Text = row["Text"].ToString();
23: node.Expanded = true;
24: Tree1.Nodes.Add(node);
25:
26: ResolveSubTree(row, node);
27: }
28: }
29: }
30:
31: private void ResolveSubTree(DataRow dataRow, TreeNode treeNode)
32: {
33: DataRow[] rows = dataRow.GetChildRows("TreeRelation");
34: if (rows.Length > 0)
35: {
36: treeNode.Expanded = true;
37: foreach (DataRow row in rows)
38: {
39: TreeNode node = new TreeNode();
40: node.Text = row["Text"].ToString();
41: treeNode.Nodes.Add(node);
42:
43: ResolveSubTree(row, node);
44: }
45: }
46: }
这段代码有点复杂,我们逐步来分析:
- 通过CreateDataTable函数拿到需要的数据源;
- 创建一个DataSet数据集,并把刚拿到的数据表加入此数据集,通过还定义了数据之间的联系(ds.Relations.Add);
- 遍历数据表中的每一行,找到没有定义ParentId的行,也即是树的根节点;
- 将这些根节点添加到树控件中(Tree1.Nodes.Add);
- 递归这些根节点,并通过数据集中数据之间的关系,找到这些根节点的所有子节点,并添加到树中。
小结
本章我们讲解了如何将各种数据源绑定到树控件,特别是将表格数据绑定到树控件的做法非常耐人寻味,不过实际项目中最常用的还是将XML文件绑定到树控件。
下一篇文章我们会讲解手风琴控件,并将使用手风琴控件和树控件组合来创建站点的菜单导航目录。