演练:在 TreeView 控件中显示分层数据
更新:2007 年 11 月
ASP.NET 的 TreeView 控件旨在以分层结构将数据显示给用户。用户可以打开单独的节点,这些节点进而可以包含子节点。TreeView 控件适合于显示 XML 数据,但可以用于任何可在层次结构中表示的数据。此演练演示使用 TreeView 控件的基础知识及显示分层数据的各种方式。
本演练中阐释的任务包括:
先决条件
若要完成本演练,您需要:
Microsoft Visual Web Developer (Visual Studio)。
Microsoft 数据访问组件 (MDAC) 2.7 版或更高版本。
如果您使用的是 Microsoft Windows XP 或 Windows Server 2003,那么您已经有了 MDAC 2.7。但是,如果使用 Microsoft Windows 2000,您可能需要升级您计算机上已经安装的 MDAC。有关更多信息,请参见 MSDN Library 中的“Microsoft Data Access Components (MDAC) Installation”(Microsoft 数据访问组件 (MDAC) 安装)。
SQL Server Northwind 数据库的访问权限。有关下载和安装 SQL Server 示例数据库 Northwind 的信息,请参见位于 Microsoft SQL Server 网站上的“Installing Sample Databases”(安装示例数据库)。
说明: 如果需要有关如何登录到运行 SQL Server 的计算机的信息,请联系服务器管理员。
如果 SQL Server 数据库与 Web 服务器位于不同的计算机上,则需要可访问 Northwind 数据库的 SQL Server 帐户的用户名和密码。
创建网站
按照下面的步骤创建一个新的网站和网页。
创建文件系统网站
打开 Visual Web Developer。
在**“文件”菜单上单击“新建”,然后单击“网站”。如果使用的是 Visual Web Developer 速成版,则在“文件”菜单上单击“新建网站”**。
出现**“新建网站”**对话框。
在**“Visual Studio 已安装的模板”下单击“ASP.NET 网站”**。
在**“位置”框中选择“文件系统”**,然后输入要保存网站网页的文件夹的名称。
例如,键入文件夹名“C:\WebSites\HierarchicalData”。
在**“语言”**列表中,单击您想使用的编程语言。
单击**“确定”**。
Visual Web Developer 创建该文件夹和一个名为 Default.aspx 的新页。
创建数据的 XML 文件
按下列这些步骤创建新的 XML 文件。
创建 XML 文件
在解决方案资源管理器中,右击网站,然后单击**“添加新项”**。
在**“添加新项”对话框中,在“标准模板”下单击“XML 文件”**。
在**“名称”框中,键入“Bookstore.xml”,然后单击“添加”**。
Visual Web Developer 创建新的 Bookstore.xml 文件,并打开代码编辑器。
复制下面的 XML 数据,然后将数据粘贴到 Bookstore.xml 文件中,改写文件中的已有内容。
<?xml version="1.0" standalone="yes"?> <bookstore> <genre name="fiction"> <book ISBN="10-000000-001"> <title>The Iliad and The Odyssey</title> <price>12.95</price> <comments> <userComment rating="4"> Best translation I've read. </userComment> <userComment rating="2"> I like other versions better. </userComment> </comments> </book> <book ISBN="10-000000-999"> <title>Anthology of World Literature</title> <price>24.95</price> <comments> <userComment rating="3"> Needs more modern literature. </userComment> <userComment rating="4"> Excellent overview of world literature. </userComment> </comments> </book> </genre> <genre name="nonfiction"> <book ISBN="11-000000-002"> <title>Computer Dictionary</title> <price>24.95</price> <comments> <userComment rating="3">A valuable resource.</userComment> </comments> </book> <book ISBN="11-000000-003"> <title>Cooking on a Budget</title> <price>23.95</price> <comments> <userComment rating="4">Delicious!</userComment> </comments> </book> </genre> </bookstore>
该 XML 文件包含有关网上书店的书籍信息。
保存 Bookstore.xml 文件,然后将其关闭。
在 TreeView 控件中显示 XML 数据
在此节中,将使用 TreeView 控件显示 XML 数据。首先,可以不用任何特殊配置显示 XML 信息。
显示 XML 数据
打开 Default.aspx 页,然后切换到“设计”视图。
在**“工具箱”中,从“导航”组中将“TreeView”**控件拖动到页面上。
右击**“TreeView”控件,然后单击“显示智能标记”**。
在**“TreeView 任务”菜单上,在“选择数据源”下拉列表中选择“新建数据源”。出现“数据源配置向导”**。
在**“应用程序从哪里获取数据?”窗口中,选择“XML 文件”。对数据源保留默认 ID。单击“确定”**。
在**“配置数据源”对话框中,在“数据文件”框中输入“~/Bookstore.xml”,然后单击“确定”**。
现在可以测试该页。
测试页面
按 Ctrl+F5 运行该页面。
折叠控件中的节点,然后将其展开。
默认情况下,节点只显示 Bookstore.xml 文件中的元素的标记名。
通过创建自定义绑定(允许对每个节点指定显示 XML 文件中的什么信息),可以自定义 TreeView 控件中显示的信息。
创建自定义绑定
在 Default.aspx 页上,右击**“TreeView”控件,然后单击“显示智能标记”**。
在**“TreeView 任务”菜单上,单击“编辑 TreeNode 数据绑定”**。
出现**“TreeView 数据绑定编辑器”**对话框。
清除**“自动生成数据绑定”**复选框,因为将要定义数据绑定。
单击**“添加”创建新的绑定,然后在“数据绑定属性”下,将“DataMember”设置为“bookstore”,并将“Text”**设置为“图书信息”。
您配置绑定以显示静态值,因为**“Bookstore”节点是 .xml 文件中最顶部的节点,并在“TreeView”**控件中只出现一次。
单击**“添加”创建第二个绑定,然后在“数据绑定属性”下,将“DataMember”设置为“genre”,并将“TextField”**设置为“name”。
这指定节点将读取 .xml 文件中的 <genre> 元素,并将该元素的 name 属性 (Attribute) 分配给 TextField 属性 (Property)。
单击**“添加”,为图书创建第三个绑定,然后在“数据绑定属性”下,将“DataMember”设置为“book”,并将“TextField”**设置为“ISBN”。
单击**“确定”**。
现在可以测试该页。
测试页面
按 Ctrl+F5 运行该页面。
此次,TreeView 控件三个与定义的绑定对应的级别。该三个级别分别是根结点(以**“图书信息”**为标签)、genre 组和 ISBN 详细信息。
可以为 XML 文件中的任何元素创建数据绑定,但是只能绑定到元素的属性、内部文本、元素名或元素的值。不能够绑定到任何嵌套元素。若要显示嵌套元素中的值,可以创建对这些元素的单独绑定。一个替代方法是使用 XSLT 转换 XML 文件,以便将内部元素转换为属性。有关更多信息及示例,请参见 XmlDataSource.TransformFile 属性。
在 TreeView 控件中显示关系数据
TreeView 控件可以显示任何类型的分层数据,即使数据的层次结构是逻辑性的(如在数据库中),而不是物理的(如在 XML 文件中)。在此节中,将使用 TreeView 控件显示 Northwind 数据库中相关表中的数据。
首先,创建对运行 SQL Server 并带有 Northwind 数据库的计算机的连接。
创建与 SQL Server 的连接
在服务器资源管理器中,右击**“数据连接”,然后单击“添加连接”**。如果使用 Visual Web Developer 速成版,则使用数据库资源管理器。
出现**“添加连接”**对话框。
如果**“数据源”列表没有显示“Microsoft SQL Server (SqlClient)”,则单击“更改”,然后在“更改数据源”对话框中选择“Microsoft SQL Server”**。
如果出现**“选择数据源”页,则在“数据源”列表中选择将要使用的数据源类型。在本演练中,数据源的类型为“Microsoft SQL Server”。在“数据提供程序”列表中,单击“用于 SQL Server 的 .NET Framework 数据提供程序”,再单击“继续”**。
说明: 如果在 Visual Web Developer 中没有显示“服务器资源管理器”选项卡,请在“视图”菜单中单击“服务器资源管理器”。如果没有显示“数据库资源管理器”选项卡,请在“视图”菜单中单击“数据库资源管理器”。
在**“添加连接”框中,在“服务器名称”**框中输入服务器名称。
在**“登录到服务器”**部分,选择适合于访问正在运行的 SQL Server 数据库的选项(集成安全性或特定 ID 和密码),如果需要,请输入用户名和密码。
选择**“保存密码”**复选框。
说明: 在成品应用程序中,不要使用“保存密码”,因为这会将用户名和密码嵌入到应用程序文件中。
在**“选择或输入数据库名称”**下输入“Northwind”。
单击**“测试连接”,并在确定该连接生效后单击“确定”**。
已经在服务器资源管理器(或数据库资源管理器)的**“数据连接”**下创建新的连接。
配置 TreeView 控件以显示数据库数据
此节中,将用数据动态填充节点。第一级别的节点将表示主控数据 — 在本例中即类别。当用户单击一个节点时,通过查询数据库(检索类别中的产品)将创建该类别的子节点。若要检索数据,可以使用数据源控件。但是,在此演练中,将以编程方式创建并执行查询。
首先,创建一个新页和新的 TreeView 控件。
创建新页和 TreeView 控件
将名为“TreeViewDynamic.aspx”的 ASP.NET 网页(Web 窗体页)添加至网站。
打开 TreeViewDynamic.aspx 页,切换到“设计”视图,然后在 Toolbox 中,从**“标准”**组中将 Label 控件拖到页上,并将该控件命名为“labelStatus”。
labelStatus 控件只用于错误报告。
在**“工具箱”中,从“导航”组中将“TreeView”**控件拖动到页面上。
右击**“TreeView”控件,单击“属性”,然后将“MaxDataBindDepth”**设置为“2”。
右击**“TreeView”控件,单击“显示智能任务”,然后在“TreeView 任务”菜单上单击“编辑节点”**。
在**“TreeView 节点编辑器”对话框中,单击带有“添加根节点”标签的图标,然后在“属性”下将“Text”设置为“产品列表”,并将“PopulateOnDemand”**设置为 true。
单击**“确定”**。
您将创建最顶部的树节点,该节点只包含静态文本。
配置 Web.config 文件
在**“工具箱”中,从“数据”**组中将 SqlDataSource 控件拖动到页面上。
选择**“SqlDataSource”控件,然后单击“显示智能标记”**。
在**“SqlDataSource 任务”菜单上单击“配置数据源”**。
**“配置数据源 - SqlDataSource1”**向导显示一个页面,在该页中可以选择连接。
在**“应用程序连接数据库应使用哪个数据连接?”框中输入在“创建与 SQL Server 的连接”中创建的连接,然后单击“下一步”**。
该向导显示一页,在该页中,可以选择将连接字符串存储在配置文件中。将连接字符串存储在配置文件中有两个优点:
比存储在页面中更安全。
可以在多个页面中使用相同的连接字符串。
选择**“是,将此连接另存为”复选框,然后单击“下一步”**。
该向导显示一页,从该页中您可以指定要从数据库中检索的数据。
在**“指定来自表或视图的列”下,在“名称”框中单击“Categories”**。
在**“列”下,选择“CategoryID”和“CategoryName”**框。
单击**“下一步”**。
单击**“完成”**。
随后,在此演练稍后定义的 RunQuery 方法中,将使用在 Web.config 文件中创建的连接字符串。将不会使用 SqlDataSource 控件。
现在,将要添加代码,以在用户单击节点时填充控件的子节点。若要以动态方式添加节点,请创建 TreeNodePopulate 事件的事件处理程序。
创建事件处理程序
右击**“TreeView”控件,然后在“属性”中单击“事件”**图标。
双击 TreeNodePopulate 事件对应的框。
Visual Web Developer 切换到“源”视图。
将下面突出显示的代码添加到处理程序中。
Protected Sub TreeView1_TreeNodePopulate(ByVal sender As Object, _ ByVal e As System.Web.UI.WebControls.TreeNodeEventArgs) _ Handles TreeView1.TreeNodePopulate If e.Node.ChildNodes.Count = 0 Then Select Case e.Node.Depth Case 0 PopulateCategories(e.Node) Case 1 PopulateProducts(e.Node) End Select End If End Sub
protected void TreeView1_TreeNodePopulate( object sender, TreeNodeEventArgs e) { if (e.Node.ChildNodes.Count == 0) { switch (e.Node.Depth) { case 0: PopulateCategories(e.Node); break; case 1: PopulateProducts(e.Node); break; } } }
当用户单击一个节点以打开该节点时,会调用此代码。因为需要在树的不同级别显示不同的数据,所以必须确定用户单击的节点深度,然后适当地填充该级别的节点。在此演练中,如果用户单击根节点(深度为 0),则调用 PopulateCategories 方法。如果用户单击类别名称(深度为 1),则调用 PopulateProducts 方法。这些方法在下一节中演示。
TreeNodeEventArgs 对象提供对当前节点的编程访问。若要填充节点,请向节点添加元素。在该代码示例中,节点被传递至方法,该方法将添加子节点。
从数据库中读取节点数据
每一个节点中显示的信息来自数据库。您必须编写代码,用于执行数据库查询、读取记录并为每条记录创建一个节点。此演练假定您使用 SQL Server Northwind 示例数据库,所以必须使用 System.Data.SqlClient 命名空间中的 ADO.NET 对象。
对于第一级别的节点(级别为 0),将显示所有可用类别的列表。您创建的代码会调用一个 RunQuery 方法,您将在此演练的稍后部分创建该方法。
添加所有类别的节点
切换到“源”视图。
如果使用单一文件页,则将下面的指令添加到代码页的顶部。
<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %>
导入命名空间将使您编写所需的代码变得更为简单。
如果使用的是一个代码隐藏页,则切换到该代码隐藏页(TreeViewDynamic.aspx.vb 或 TreeViewDynamic.aspx.cs),并将下面的代码行添加到代码文件的顶部(位于类声明之外)。
Imports System.Data Imports System.Data.SqlClient
using System.Data; using System.Data.SqlClient;
确保页仍在“源”视图中。
将下面的方法添加到页代码中。
Sub PopulateCategories(ByVal node As TreeNode) Dim sqlQuery As New SqlCommand( _ "Select CategoryName, CategoryID From Categories") Dim ResultSet As DataSet ResultSet = RunQuery(sqlQuery) If ResultSet.Tables.Count > 0 Then Dim row As DataRow For Each row In ResultSet.Tables(0).Rows Dim NewNode As TreeNode = New _ TreeNode(row("CategoryName").ToString(), _ row("CategoryID").ToString()) NewNode.PopulateOnDemand = True NewNode.SelectAction = TreeNodeSelectAction.Expand node.ChildNodes.Add(NewNode) Next End If End Sub
void PopulateCategories(TreeNode node) { SqlCommand sqlQuery = new SqlCommand( "Select CategoryName, CategoryID From Categories"); DataSet resultSet; resultSet = RunQuery(sqlQuery); if (resultSet.Tables.Count > 0) { foreach (DataRow row in resultSet.Tables[0].Rows) { TreeNode NewNode = new TreeNode(row["CategoryName"].ToString(), row["CategoryID"].ToString()); NewNode.PopulateOnDemand = true; NewNode.SelectAction = TreeNodeSelectAction.Expand; node.ChildNodes.Add(NewNode); } } }
该代码创建 SqlCommand 对象,该对象封装查询的文本。代码将该对象传递至一个随后将要编写的方法,该方法执行数据库查询,并返回 DataSet 对象。此代码然后遍历 DataSet 对象中的记录,并为每条记录创建一个新的节点,以数据库信息设置该节点的文本和值。然后,代码将每个节点的 PopulateOnDemand 属性设置为 true,以使节点在被单击时将引发其 TreeNodePopulate 事件。SelectAction 属性被设置,以使节点在默认情况下展开。
第二级别的节点将显示每个类别的产品。由于此原因,填充产品节点需要参数化查询,以使您能够检索当前类别的产品,并以恰当方式填充子节点。
为产品添加节点
将下面的方法添加到页代码中。
Sub PopulateProducts(ByVal node As TreeNode) Dim sqlQuery As New SqlCommand sqlQuery.CommandText = "Select ProductName From Products " & _ " Where CategoryID = @categoryid" sqlQuery.Parameters.Add("@categoryid", SqlDbType.Int).Value = _ node.Value Dim ResultSet As DataSet = RunQuery(sqlQuery) If ResultSet.Tables.Count > 0 Then Dim row As DataRow For Each row In ResultSet.Tables(0).Rows Dim NewNode As TreeNode = New _ TreeNode(row("ProductName").ToString()) NewNode.PopulateOnDemand = False NewNode.SelectAction = TreeNodeSelectAction.None node.ChildNodes.Add(NewNode) Next End If End Sub
void PopulateProducts(TreeNode node) { SqlCommand sqlQuery = new SqlCommand(); sqlQuery.CommandText = "Select ProductName From Products " + " Where CategoryID = @categoryid"; sqlQuery.Parameters.Add("@categoryid", SqlDbType.Int).Value = node.Value; DataSet ResultSet = RunQuery(sqlQuery); if (ResultSet.Tables.Count > 0) { foreach (DataRow row in ResultSet.Tables[0].Rows) { TreeNode NewNode = new TreeNode(row["ProductName"].ToString()); NewNode.PopulateOnDemand = false; NewNode.SelectAction = TreeNodeSelectAction.None; node.ChildNodes.Add(NewNode); } } }
此代码与用以填充类别节点的代码类似。不同之一是 SqlCommand 对象配置有一个参数,在运行时,以用户单击的节点(即选择的类别)的值来设置该参数。另一不同之处是 PopulateOnDemand 属性设置为 false。这导致产品节点显示后不带有展开按钮,这是必须的,因为产品下再没有节点。
最后步骤是创建一个方法,该方法执行查询并返回数据集。
执行查询
将下面的子例程添加至页。
Function RunQuery(ByVal sqlQuery As SqlCommand) As DataSet Dim connectionString As String connectionString = _ ConfigurationManager.ConnectionStrings _ ("NorthwindConnectionString").ConnectionString Dim dbConnection As New SqlConnection dbConnection.ConnectionString = connectionString Dim dbAdapter As New SqlDataAdapter dbAdapter.SelectCommand = sqlQuery sqlQuery.Connection = dbConnection Dim resultsDataSet As DataSet = New DataSet Try dbAdapter.Fill(resultsDataSet) Catch ex As Exception labelStatus.Text = "Unable to connect to SQL Server." End Try Return resultsDataSet End Function
private DataSet RunQuery(SqlCommand sqlQuery) { string connectionString = ConfigurationManager.ConnectionStrings ["NorthwindConnectionString"].ConnectionString; SqlConnection DBConnection = new SqlConnection(connectionString); SqlDataAdapter dbAdapter = new SqlDataAdapter(); dbAdapter.SelectCommand = sqlQuery; sqlQuery.Connection = DBConnection; DataSet resultsDataSet = new DataSet(); try { dbAdapter.Fill(resultsDataSet); } catch { labelStatus.Text = "Unable to connect to SQL Server."; } return resultsDataSet; }
此代码根据传递至它的 SqlCommand 对象创建数据适配器。然后,该代码使用适配器创建并填充数据集。
现在可以测试该页。
测试页面
按 Ctrl+F5 运行该页面。
TreeView 控件随类别和产品的列表一起显示。
单击一个类别,确认该类别可折叠及展开,以显示每种类别的产品列表。
后续步骤
此演练既使用分层 XML 数据又使用关系数据库来填充 TreeView 控件。可以使用 TreeView 控件,以便使用网站导航信息以及作为表格(列表)数据的 XML 数据。
有关网站导航的详细信息,请参见 演练:向网站添加站点导航。
有关更多使用 XML 数据的详细信息,请参见 演练:创建网页以显示 XML 数据。