演练:在 TreeView 控件中显示分层数据

更新:2007 年 11 月

ASP.NET 的 TreeView 控件旨在以分层结构将数据显示给用户。用户可以打开单独的节点,这些节点进而可以包含子节点。TreeView 控件适合于显示 XML 数据,但可以用于任何可在层次结构中表示的数据。此演练演示使用 TreeView 控件的基础知识及显示分层数据的各种方式。

本演练中阐释的任务包括:

  • 使用 TreeView 控件显示 XML 数据。

  • 自定义 TreeView 控件的显示。

  • 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”(安装示例数据库)

    7a9swst5.alert_note(zh-cn,VS.90).gif说明:

    如果需要有关如何登录到运行 SQL Server 的计算机的信息,请联系服务器管理员。

  • 如果 SQL Server 数据库与 Web 服务器位于不同的计算机上,则需要可访问 Northwind 数据库的 SQL Server 帐户的用户名和密码。

创建网站

按照下面的步骤创建一个新的网站和网页。

创建文件系统网站

  1. 打开 Visual Web Developer。

  2. 在**“文件”菜单上单击“新建”,然后单击“网站”。如果使用的是 Visual Web Developer 速成版,则在“文件”菜单上单击“新建网站”**。

    出现**“新建网站”**对话框。

  3. 在**“Visual Studio 已安装的模板”下单击“ASP.NET 网站”**。

  4. 在**“位置”框中选择“文件系统”**,然后输入要保存网站网页的文件夹的名称。

    例如,键入文件夹名“C:\WebSites\HierarchicalData”。

  5. 在**“语言”**列表中,单击您想使用的编程语言。

  6. 单击**“确定”**。

    Visual Web Developer 创建该文件夹和一个名为 Default.aspx 的新页。

创建数据的 XML 文件

按下列这些步骤创建新的 XML 文件。

创建 XML 文件

  1. 在解决方案资源管理器中,右击网站,然后单击**“添加新项”**。

  2. 在**“添加新项”对话框中,在“标准模板”下单击“XML 文件”**。

  3. 在**“名称”框中,键入“Bookstore.xml”,然后单击“添加”**。

    Visual Web Developer 创建新的 Bookstore.xml 文件,并打开代码编辑器。

  4. 复制下面的 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 文件包含有关网上书店的书籍信息。

  5. 保存 Bookstore.xml 文件,然后将其关闭。

在 TreeView 控件中显示 XML 数据

在此节中,将使用 TreeView 控件显示 XML 数据。首先,可以不用任何特殊配置显示 XML 信息。

显示 XML 数据

  1. 打开 Default.aspx 页,然后切换到“设计”视图。

  2. 在**“工具箱”中,从“导航”组中将“TreeView”**控件拖动到页面上。

  3. 右击**“TreeView”控件,然后单击“显示智能标记”**。

  4. 在**“TreeView 任务”菜单上,在“选择数据源”下拉列表中选择“新建数据源”。出现“数据源配置向导”**。

  5. 在**“应用程序从哪里获取数据?”窗口中,选择“XML 文件”。对数据源保留默认 ID。单击“确定”**。

  6. 在**“配置数据源”对话框中,在“数据文件”框中输入“~/Bookstore.xml”,然后单击“确定”**。

现在可以测试该页。

测试页面

  1. 按 Ctrl+F5 运行该页面。

  2. 折叠控件中的节点,然后将其展开。

    默认情况下,节点只显示 Bookstore.xml 文件中的元素的标记名。

通过创建自定义绑定(允许对每个节点指定显示 XML 文件中的什么信息),可以自定义 TreeView 控件中显示的信息。

创建自定义绑定

  1. 在 Default.aspx 页上,右击**“TreeView”控件,然后单击“显示智能标记”**。

  2. 在**“TreeView 任务”菜单上,单击“编辑 TreeNode 数据绑定”**。

    出现**“TreeView 数据绑定编辑器”**对话框。

  3. 清除**“自动生成数据绑定”**复选框,因为将要定义数据绑定。

  4. 单击**“添加”创建新的绑定,然后在“数据绑定属性”下,将“DataMember”设置为“bookstore”,并将“Text”**设置为“图书信息”。

    您配置绑定以显示静态值,因为**“Bookstore”节点是 .xml 文件中最顶部的节点,并在“TreeView”**控件中只出现一次。

  5. 单击**“添加”创建第二个绑定,然后在“数据绑定属性”下,将“DataMember”设置为“genre”,并将“TextField”**设置为“name”。

    这指定节点将读取 .xml 文件中的 <genre> 元素,并将该元素的 name 属性 (Attribute) 分配给 TextField 属性 (Property)。

  6. 单击**“添加”,为图书创建第三个绑定,然后在“数据绑定属性”下,将“DataMember”设置为“book”,并将“TextField”**设置为“ISBN”。

  7. 单击**“确定”**。

现在可以测试该页。

测试页面

  • 按 Ctrl+F5 运行该页面。

    此次,TreeView 控件三个与定义的绑定对应的级别。该三个级别分别是根结点(以**“图书信息”**为标签)、genre 组和 ISBN 详细信息。

可以为 XML 文件中的任何元素创建数据绑定,但是只能绑定到元素的属性、内部文本、元素名或元素的值。不能够绑定到任何嵌套元素。若要显示嵌套元素中的值,可以创建对这些元素的单独绑定。一个替代方法是使用 XSLT 转换 XML 文件,以便将内部元素转换为属性。有关更多信息及示例,请参见 XmlDataSource.TransformFile 属性。

在 TreeView 控件中显示关系数据

TreeView 控件可以显示任何类型的分层数据,即使数据的层次结构是逻辑性的(如在数据库中),而不是物理的(如在 XML 文件中)。在此节中,将使用 TreeView 控件显示 Northwind 数据库中相关表中的数据。

首先,创建对运行 SQL Server 并带有 Northwind 数据库的计算机的连接。

创建与 SQL Server 的连接

  1. 在服务器资源管理器中,右击**“数据连接”,然后单击“添加连接”**。如果使用 Visual Web Developer 速成版,则使用数据库资源管理器。

    出现**“添加连接”**对话框。

    • 如果**“数据源”列表没有显示“Microsoft SQL Server (SqlClient)”,则单击“更改”,然后在“更改数据源”对话框中选择“Microsoft SQL Server”**。

    • 如果出现**“选择数据源”页,则在“数据源”列表中选择将要使用的数据源类型。在本演练中,数据源的类型为“Microsoft SQL Server”。在“数据提供程序”列表中,单击“用于 SQL Server 的 .NET Framework 数据提供程序”,再单击“继续”**。

    7a9swst5.alert_note(zh-cn,VS.90).gif说明:

    如果在 Visual Web Developer 中没有显示“服务器资源管理器”选项卡,请在“视图”菜单中单击“服务器资源管理器”。如果没有显示“数据库资源管理器”选项卡,请在“视图”菜单中单击“数据库资源管理器”

  2. 在**“添加连接”框中,在“服务器名称”**框中输入服务器名称。

  3. 在**“登录到服务器”**部分,选择适合于访问正在运行的 SQL Server 数据库的选项(集成安全性或特定 ID 和密码),如果需要,请输入用户名和密码。

  4. 选择**“保存密码”**复选框。

    7a9swst5.alert_note(zh-cn,VS.90).gif说明:

    在成品应用程序中,不要使用“保存密码”,因为这会将用户名和密码嵌入到应用程序文件中。

  5. 在**“选择或输入数据库名称”**下输入“Northwind”。

  6. 单击**“测试连接”,并在确定该连接生效后单击“确定”**。

    已经在服务器资源管理器(或数据库资源管理器)的**“数据连接”**下创建新的连接。

配置 TreeView 控件以显示数据库数据

此节中,将用数据动态填充节点。第一级别的节点将表示主控数据 — 在本例中即类别。当用户单击一个节点时,通过查询数据库(检索类别中的产品)将创建该类别的子节点。若要检索数据,可以使用数据源控件。但是,在此演练中,将以编程方式创建并执行查询。

首先,创建一个新页和新的 TreeView 控件。

创建新页和 TreeView 控件

  1. 将名为“TreeViewDynamic.aspx”的 ASP.NET 网页(Web 窗体页)添加至网站。

  2. 打开 TreeViewDynamic.aspx 页,切换到“设计”视图,然后在 Toolbox 中,从**“标准”**组中将 Label 控件拖到页上,并将该控件命名为“labelStatus”。

    labelStatus 控件只用于错误报告。

  3. 在**“工具箱”中,从“导航”组中将“TreeView”**控件拖动到页面上。

  4. 右击**“TreeView”控件,单击“属性”,然后将“MaxDataBindDepth”**设置为“2”。

  5. 右击**“TreeView”控件,单击“显示智能任务”,然后在“TreeView 任务”菜单上单击“编辑节点”**。

  6. 在**“TreeView 节点编辑器”对话框中,单击带有“添加根节点”标签的图标,然后在“属性”下将“Text”设置为“产品列表”,并将“PopulateOnDemand”**设置为 true。

  7. 单击**“确定”**。

    您将创建最顶部的树节点,该节点只包含静态文本。

配置 Web.config 文件

  1. 在**“工具箱”中,从“数据”**组中将 SqlDataSource 控件拖动到页面上。

  2. 选择**“SqlDataSource”控件,然后单击“显示智能标记”**。

  3. 在**“SqlDataSource 任务”菜单上单击“配置数据源”**。

    **“配置数据源 - SqlDataSource1”**向导显示一个页面,在该页中可以选择连接。

  4. 在**“应用程序连接数据库应使用哪个数据连接?”框中输入在“创建与 SQL Server 的连接”中创建的连接,然后单击“下一步”**。

    该向导显示一页,在该页中,可以选择将连接字符串存储在配置文件中。将连接字符串存储在配置文件中有两个优点:

    • 比存储在页面中更安全。

    • 可以在多个页面中使用相同的连接字符串。

  5. 选择**“是,将此连接另存为”复选框,然后单击“下一步”**。

    该向导显示一页,从该页中您可以指定要从数据库中检索的数据。

  6. 在**“指定来自表或视图的列”下,在“名称”框中单击“Categories”**。

  7. 在**“列”下,选择“CategoryID”“CategoryName”**框。

  8. 单击**“下一步”**。

  9. 单击**“完成”**。

    随后,在此演练稍后定义的 RunQuery 方法中,将使用在 Web.config 文件中创建的连接字符串。将不会使用 SqlDataSource 控件。

现在,将要添加代码,以在用户单击节点时填充控件的子节点。若要以动态方式添加节点,请创建 TreeNodePopulate 事件的事件处理程序。

创建事件处理程序

  1. 右击**“TreeView”控件,然后在“属性”中单击“事件”**图标。

  2. 双击 TreeNodePopulate 事件对应的框。

    Visual Web Developer 切换到“源”视图。

  3. 将下面突出显示的代码添加到处理程序中。

    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 方法,您将在此演练的稍后部分创建该方法。

添加所有类别的节点

  1. 切换到“源”视图。

  2. 如果使用单一文件页,则将下面的指令添加到代码页的顶部。

    <%@ Import Namespace="System.Data" %>
    <%@ Import Namespace="System.Data.SqlClient" %>
    

    导入命名空间将使您编写所需的代码变得更为简单。

  3. 如果使用的是一个代码隐藏页,则切换到该代码隐藏页(TreeViewDynamic.aspx.vb 或 TreeViewDynamic.aspx.cs),并将下面的代码行添加到代码文件的顶部(位于类声明之外)。

    Imports System.Data
    Imports System.Data.SqlClient
    
    using System.Data;
    using System.Data.SqlClient;
    
  4. 确保页仍在“源”视图中。

  5. 将下面的方法添加到页代码中。

    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 对象创建数据适配器。然后,该代码使用适配器创建并填充数据集。

现在可以测试该页。

测试页面

  1. 按 Ctrl+F5 运行该页面。

    TreeView 控件随类别和产品的列表一起显示。

  2. 单击一个类别,确认该类别可折叠及展开,以显示每种类别的产品列表。

后续步骤

此演练既使用分层 XML 数据又使用关系数据库来填充 TreeView 控件。可以使用 TreeView 控件,以便使用网站导航信息以及作为表格(列表)数据的 XML 数据。

请参见

任务

演练:创建网页以显示 XML 数据

其他资源

如何:确保使用数据源控件时连接字符串的安全