65.9K
CodeProject 正在变化。 阅读更多。
Home

用于标准 SQL 数据的自定义 TreeView

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (4投票s)

2007年12月5日

CPOL

5分钟阅读

viewsIcon

30190

downloadIcon

589

一个 ASP.NET TreeView,兼容标准 DataViews,并完全支持声明式语法。

引言

ASP.NET 1.0 没有提供树形视图控件。我想这是很多人需要的,因为微软在 ASP.NET 2 中引入了这样的控件。然而,MS 的 TreeView 有一个特别之处,让它在处理“标准”数据时使用起来很不方便:这个 TreeView 需要一个实现 IHierarchicalEnumerable 的数据源(例如:XML 数据……)。当你想要将这个 TreeView 与 SQL 数据绑定时,你必须编写一些代码(即,你不能使用声明式语法,这是 ASP.NET 的标志性功能)。这里介绍的 CustomTreeView 提供了从标准 DataView 显示数据的可能性,并且仍然可以受益于 ASP.NET 的声明式语法(通过 NodeTemplate 模板)。

背景

CustomTreeView 是一个模板化数据绑定控件。我不会解释它是如何工作的,因为你可以在 MSDN(或在我之前的文章 NestedRepeater)中找到完整的细节。

使用代码

数据源

我将显示一个名为 GEOGRAPHY 的表的数据。这个表需要主键/外键关系。正是这种关系创建了树形视图渲染的层次结构。在这里,GEO_PARENT 引用 GEO_ID 来创建父子关系。

Screenshot - ASP.NET_TreeView0.JPG

代码隐藏

GEOGRAPHY 中的数据存储在 DataView 中。在 DataView 中创建了一个 DataRelation,然后使用 RowFilter 过滤器来选择最顶层的节点。代码如下:

SqlDataAdapter da = new SqlDataAdapter("select * from GEOGRAPHY",
            ConfigurationManager.ConnectionStrings[1].ConnectionString);

DataSet ds = new DataSet();
da.Fill(ds, "geo");
ds.Relations.Add("FatherChild", ds.Tables["geo"].Columns["GEO_ID"],
    ds.Tables["geo"].Columns["GEO_PARENT"]);

myTree.DataSource = ds.Tables["geo"].DefaultView;
myTree.RowFilter = "GEO_PARENT is null";
myTree.RelationName = "FatherChild";

myTree.DataBind();

请注意,DataRelation 被赋予了一个名称,该名称通过 RelationName 属性提供给 CustomTreeView

在 Web 窗体上

在本例中,我将显示一个包含国家、地区、城市……以及一些示例功能(链接和按钮)的树。要在你的 Web 窗体上使用 CustomTreeView,你必须首先注册程序集:

<%@ Register tagprefix="WCC" 
    Namespace="WebCustomControls" Assembly="WebControls"%>

如果你使用 Visual Studio,你必须在你的项目中添加一个对“WebControls.dll”的引用,才能使此行正常工作。然后:

<WCC:CustomTreeView id=myTree runat="server">
  <NodeTemplate>
    <%# (Container.DataItem as DataRow)["GEO_NAME"]%>
        <%# String.Format("<a href=\"http://www.google.com/search?q={0}\" 
             target=\"_blank\">Google</a>",
                 (Container.DataItem as DataRowView)[1])%>
    <asp:ImageButton runat=server ID=BnSend 
       ImageUrl="~/IMGs/Valid.gif" 
       CommandArgument=<%# (Container.DataItem as DataRowView)[0] %> 
       ImageAlign=AbsMiddle 
       OnCommand=DoValid />
  </NodeTemplate>
</WCC:/NodeTemplate>

使用 NodeTemplate 类,你可以为每个节点添加任何控件。此模板将为每个节点实例化。

为了展开/折叠节点,你必须点击“文件夹”图标(见下文示例)。这让你有机会使用节点内容的“Click”事件。

图像

为了简单起见,这里的图像 URL 是硬编码的。所有图像都位于网站根文件夹下的“IMGs”目录中。你可以通过在 /IMGs 文件夹中提供自己的图像(但保留硬编码的名称)来显示你自己的图像。但请确保保留“menu_bar_invisible.gif”图像不变。如果你决定使用自己的图像,请注意所有图像必须尺寸相同。你还可以添加一些额外的属性,以便宿主 Web 窗体能够动态指定 URL。当你在 NodeTemplate 中添加控件时,请确保用于渲染树分支的图像足够大,否则这些图像之间会出现空白,从而“破坏”树形视图,如下所示:

Screenshot - ASP.NET_TreeView1.JPG

在本例中,图像的高度只有 16 像素,而按钮至少需要 24 像素。在这里,你必须提供自己尺寸更大的图像。

工作原理

有几点需要解释。

m_lstColumnNeedVisualRendering

我们使用此列表来知道在哪里必须显示“menu_bar.gif”以及在哪里必须显示“menu_bar_invisible.gif”。这两个图像用于在视觉上渲染树(树的分支)。困难之处在于,一旦我们到达了节点的最后一个子节点,即使该最后一个子节点也有子节点,也必须没有分隔条。

Screenshot - ASP.NET_TreeView2.JPG

在上面的示例中,即使“France”下的子节点也属于“Europe”,“Europe”下的垂直分隔条在到达“France”时就停止了。m_lstColumnNeedVisualRendering 是一个布尔值列表,列表中的每个布尔值对应树结构中的一列。如果列表中位置为 i 的元素为 true,则显示列 i 的分隔条,否则显示一个“不可见的 GIF”。

m_listOpenedNodes

此列表允许我们知道在用户提交包含 CustomTreeView 的表单时,哪些节点被展开了。这样,我们就可以渲染 CustomTreeView,使其状态与客户端浏览器上的状态完全相同。在生成的 HTML 中,此列表存储在一个隐藏字段中(<input type=hidden...>)。当用户展开节点时,JavaScript 客户端代码会将该节点添加到列表中(如果用户随后关闭该节点,则会将其移除)。在回发时,此隐藏字段的内容是通过使用 Request.Form 集合读取的。这是因为隐藏字段的视图状态不由 .NET 处理(因为它是在控件上动态添加的)。

JavaScript

在每个节点下方,所有子节点都放置在一个 SPAN 中。如果节点的 ID 为 i,则 SPAN 的 ID 为“Level_i”。节点的“文件夹”图标的 ID 为“Image_i”。当用户点击一个节点时,JavaScript 函数 ExpandNode 将显示/隐藏合适的 SPAN,并更改文件夹图标以反映更改。

结论

所以输出如下:

Screenshot - ASP.NET_TreeView3.JPG

在每个节点旁边,都添加了一个超链接,允许用户显示该项目的 Google 页面。还添加了一个 ImageButton。点击此按钮将提交 Web 窗体。一旦 Click 事件在服务器端被处理,树形视图将在用户浏览器的相同状态下显示。

© . All rights reserved.