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

一个带模板的 ASP.NET AJAX TreeView 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (24投票s)

2008 年 12 月 7 日

Ms-PL

6分钟阅读

viewsIcon

240316

downloadIcon

7567

一个带模板、事件绑定、客户端和服务器事件等的 ASP.NET AJAX TreeView 控件...

TreeView2.png Odyssey TreeView3.png

引言

OdcTreeView 是一个具有分层数据绑定的、支持模板的 ASP.NET AJAX 服务器控件。

本文涵盖以下主题

  • 静态节点
  • 按需加载
  • IHierarchicalDataSource 绑定
  • 模板
  • NodeBinding 事件
  • AJAX 服务器控件
  • JavaScript 命名空间、类、属性和自定义事件
  • 自定义样式

静态节点

与 ASP.NET 的 TreeView 类似,您可以选择使用数据源来检索节点,或者像这样直接在 ASPX 源代码中指定它们

<odc:OdcTreeView ID="OdcTreeView1" 
    runat="server" EnableViewState="true"> 
<Nodes>
<odc:OdcTreeNode Text="1">
<odc:OdcTreeNode Text="1.1" ImageUrl="~/ColorHS.png" />
<odc:OdcTreeNode Text="1.2" />
</odc:OdcTreeNode>
<odc:OdcTreeNode Text="2" />
<odc:OdcTreeNode Text="3">
<odc:OdcTreeNode Text="3.1" />
<odc:OdcTreeNode Text="3.2" />
<odc:OdcTreeNode Text="3.3">
<odc:OdcTreeNode Text="3.3.1" />
<odc:OdcTreeNode Text="3.3.2" />
<odc:OdcTreeNode Text="3.3.3" />
<odc:OdcTreeNode Text="3.3.4" ImageUrl="~/ColorHS.png" />
<odc:OdcTreeNode Text="3.3.5" ShowCheckBox="true" />
<odc:OdcTreeNode Text="3.3.6" 
  ShowCheckBox="true" IsChecked="true" />
</odc:OdcTreeNode>
</odc:OdcTreeNode>
<odc:OdcTreeNode Text="4">
<odc:OdcTreeNode Text="4.1" />
<odc:OdcTreeNode Text="4.2" 
  PopulateOnDemand="true" IsExpanded="true" />
<odc:OdcTreeNode Text="4.3" />
<odc:OdcTreeNode Text="4.4" />
<odc:OdcTreeNode Text="4.5" />
</odc:OdcTreeNode>
</Nodes>
</odc:OdcTreeView>

按需加载

结合静态节点,您还可以启用按需加载,这意味着您不必一次性声明所有可能的节点,并且只在父节点第一次展开时才添加子节点。因此,您只需将 OdcTreeNodePopulateOnDemand 属性设置为 true,这将导致 OdcTreeView 触发一个 NodePopulate 事件。此事件可以这样实现

protected void OdcTreeView1_NodePopulate(object sender, OdcTreeNodeEventArgs e)
{
    OdcTreeNode node = e.Node;
    node.PopulateOnDemand = false;
    for (int i = 1; i < 6; i++)
    {
        OdcTreeNode sub = new OdcTreeNode();
        sub.Text = node.Text + "." + i.ToString();
        sub.IsExpanded = false;
        sub.PopulateOnDemand = i==3;
        node.ChildNodes.Add(sub);
    }
}

请注意,此方法还将 PopulateOnDemand 设置为 false,因此当节点折叠后再次展开时,不会再次触发 NodePopulate 事件,因为它将状态与已填充的树节点一起存储在视图状态中。

数据绑定

当然,OdcTreeNode 也支持与任何 IHierarchicalDataSource 进行数据绑定。您可以声明一个 DataSourceID,指向一个实现 IHierarchicalDataSourceDataSource,或者在运行时将 DataSource 属性设置为任何 IHierarchicalDataSource。此外,您至少需要声明一个 OdcTreeNodeBinding 类,除非您不使用自定义节点模板来直接绑定嵌入式控件。此类指定如何将数据项的属性绑定到 OdcTreeNodeItem 的属性,或者声明 OdcTeeNodeItem 应该被赋予哪些默认值。如果您不设置 OdcTreeNodeBinding.Level 的值,则此绑定将用作所有节点的模板。但是,如果您指定了一个级别,则此 OdcTreeNodeBinding 只会影响该分层级别的节点。

此示例演示了使用 XML 文件进行声明式数据绑定

<odc:OdcTreeView ID="OdcTreeView1" 
  runat="server" DataSourceID="XmlDataSource1">
<TreeNodeBindings>
<odc:OdcTreeNodeBinding Level="0" TextField="Title"/>
<odc:OdcTreeNodeBinding Level="1" TextField="Title" />
<odc:OdcTreeNodeBinding Level="2" TextField="Title" 
  ShowCheckBox="true" IsChecked="true" />
<odc:OdcTreeNodeBinding Level="3" TextField="Value" 
  ShowCheckBox="true" IsChecked="false" />
</TreeNodeBindings>
</odc:OdcTreeView>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" 
  DataFile="~/Xml.xml"></asp:XmlDataSource>

模板

OdcTreeView 知道三种不同的模板

  • NodeTemplate - 每个节点的单个数据模板。
  • EditNodeTemplate - 处于编辑模式的节点的模板,一次只能有一个。
  • ContextMenuTemplate - 当您右键单击节点时出现的上下文菜单的模板(除非您使用的是 Opera 浏览器)。如果您不指定 NodeTemplateEditNodeTemplateOdcTreeView 会生成一个默认模板。

以下示例演示了如何使用模板

<odc:OdcTreeView ID="treeView" runat="server" AutoPostBack="False" >
<ContextMenuTemplate>
<asp:LinkButton ID="btnOkay" runat="server" CommandName="ok" Text="Okay" /><br />
<asp:LinkButton ID="btnCancel" runat="server" CommandName="cancel" Text="Cancel" />
</ContextMenuTemplate>
<EditNodeTemplate>
<asp:TextBox runat="server" ID="tbText" Text='<%# Bind("Text") %>' />
<asp:LinkButton ID="btnRename" runat="server" CommandName="rename" Text="Rename" />
<asp:LinkButton ID="btnCancel" runat="server" CommandName="cancel" Text="Cancel" />
</EditNodeTemplate>
<NodeTemplate>
<%# Container.Node.Text %><asp:LinkButton 
  Style="padding-left: 4px" ID="btnAdd" runat="server"
CommandName="add" Text="Add" />
<asp:LinkButton ID="btnRemove" runat="server" CommandName="remove" Text="Remove" />
<asp:LinkButton ID="btnEdit" runat="server" CommandName="edit" Text="Edit" />
</NodeTemplate>
</odc:OdcTreeView>

正如您所见,有两种方法用于绑定 OdcTreeNode.Text 属性

  1. <%# Bind("Text") %>
  2. 使用 Bind() 可让您访问数据绑定项的属性,而不是 OdcTreeNode 本身,这意味着节点必须绑定到一个具有 Text 属性的对象。如果未进行数据绑定,Bind() 将从节点本身检索属性。

  3. <%# Container.Node.Text %>
  4. 这是直接访问树节点属性的用法。由于 Container 的类型是 OdcTreeNodeContainer,它公开了 Node,而 DataItem 将是数据绑定对象。

OdcTreeNodeBinding 还提供了一个 NodeTemplate 来指定。这使您可以修改不同级别节点的样式。以下示例演示了如何将按钮应用于根节点

<odc:OdcTreeView ID="OdcTreeView1" runat="server" DataSourceID="XmlDataSource1">
<TreeNodeBindings>
<odc:OdcTreeNodeBinding Level="0" TextField="Title">
<NodeTemplate>
<asp:Button runat="server" Text="this is the root" UseSubmitBehavior="False" />
</NodeTemplate>
</odc:OdcTreeNodeBinding>
<odc:OdcTreeNodeBinding Level="1" TextField="Title" />
<odc:OdcTreeNodeBinding Level="2" TextField="Title" 
   ShowCheckBox="true" IsChecked="true" />
<odc:OdcTreeNodeBinding Level="3" TextField="Value" 
   ShowCheckBox="true" IsChecked="false" />
</TreeNodeBindings>
</odc:OdcTreeView>

NodeBinding 事件

甚至可以通过名为 NodeBinding 的事件将 NodeBindings 集合中的任何 OdcTreeNodeBinding 附加到由事件确定的节点。这两种方式都适用于数据绑定和静态节点,或者按需加载。因此,您可以使用任何您想要的术语来改变节点的模板。

以下示例说明了如何使用 NodeBinding

ASPX 部分
<odc:OdcTreeView ID="OdcTreeView1" runat="server" EnableViewState="true" Font-Size="9"
Font-Names="Arial" EnableDragDrop="true" AutoPostBack="false" DisableTextSelection="true"
EnableClientExpand="true" AllowNodeEditing="false" ExpandDepth="7" 
onnodebinding="OdcTreeView1_NodeBinding" 
onnodepopulate="OdcTreeView1_NodePopulate">
<TreeNodeBindings>
<odc:OdcTreeNodeBinding Name="Root" ShowCheckBox="true">
<NodeTemplate>
<asp:Label ID="Label2" isText="true" runat="server" 
   Text="<%# Container.Node.Text %>" />
</NodeTemplate>
</odc:OdcTreeNodeBinding>
</TreeNodeBindings>
代码部分
/// <summary>
/// Determine what OdcTreeNodeBinding to apply for for a node.
/// </summary>
protected void OdcTreeView1_NodeBinding(object sender, 
               Odyssey.Web.TreeView.OdcTreeNodeBindingEventArgs e)
{
OdcTreeNode node = e.Node;
// if the node has child nodes, apply a different NodeBinding
// that contains a different node template to the node:
if (node.HasChildNodes) e.Binding = e.Bindings.GetNamedBinding("Root");
}

AJAX 服务器控件

OdcTreeView 是一个 AJAX 服务器控件,这意味着它需要在页面上包含一个 ScriptManager。通过 AJAX,树视图可以在浏览器中直接执行许多操作,而无需任何回发。因此,可以选择一个节点,并在浏览器中完全展开和折叠节点。如果您将 AllowNodeEditing 设置为 true,甚至可以在浏览器中编辑节点文本。OdcTreeView 会向页面添加一个隐藏字段,并将其注册到控件的客户端部分。客户端使用 JavaScript 更新隐藏字段中的更改,并在回发时,服务器端读取隐藏字段的值,并更新值以反映客户端所做的更改。但是,如果您希望在选择、编辑、展开或折叠节点时发生回发,则必须将 AutoPostBack 设置为 false。请注意,当展开具有 PopulateOnDemand 设置为 true 的节点时,始终会发生回发。

JavaScript 命名空间、类、属性和自定义事件

OdcTreeView 的客户端部分包含一个名为 TreeViewControl 的 JavaScript 类、一个 TreeNode 类和一个 TreeContextMenuEventArgs 类。所有类都属于 Odyssey.Web 命名空间。我不想详细介绍如何使 JavaScript 看起来像 C#,拥有命名空间、类、接口、属性和事件,因为有很多好的书籍涵盖了这些功能。TreeViewControl 是 AJAX 控件的客户端部分。它捕获 HTML 事件,并根据引发的事件执行所需的回发。您还可以为 OdcTreeView 指定客户端事件,这些事件不会在服务器端执行,而是通过调用 JavaScript 函数在客户端执行。目前可用的事件如下

  • ClientContextMenuOpening(treeView, e);
  • 此事件在上下文菜单打开之前发生,允许您修改上下文菜单的外观或行为,并公开两个参数

    • treeView,类型为 Odyssey.Web.TreeViewControl
    • e,类型为 Odyssey.Web.TreeContextMenuEventArgse 包含将要为其打开上下文菜单的节点以及覆盖上下文菜单本身的 HTML 元素。由于 e 派生自 CancelEventArgs,您也可以取消打开上下文菜单。
  • ClientNodeSelectionChanged(node, e);
  • 当节点被选中时发生。

  • ClientNodeExpanded(node,e);
  • 当节点被展开时发生。

  • ClientNodeCollapsed(node, e);
  • 当节点被折叠时发生。

  • ClientNodeCheckedChanged(node, e);
  • 当节点更改其 get_checked() 状态时发生。

  • ClienEditModeChanged(node, e);
  • 当节点更改其 isEditable() 状态时发生。

以下代码片段说明了如何使用客户端事件以及如何从 AJAX 类中检索属性

<odc:OdcTreeView ID="OdcTreeView1" runat="server" EnableViewState="true" 
ClientNodeTextChanged="nodeTextChanged"
ClientNodeExpanded = "expanded"
ClientContextMenuOpening="myContextMenu" 
ClientNodeCollapsed="collapsed"
ClientNodeSelectionChanged="nodeSelected"
 
…

</form>
<script type="text/javascript">
function expanded(node, e) {
if (node.getText()>="4")
  alert("Expanded: "+node.getText());
}


function collapsed(node, e) {
if (node.getText() >= "4")
  alert("Collapsed: " + node.getText());
}
 

function nodeTextChanged(node, e) {
var txt = node.getText();
var e = document.getElementsByName("custom");
e[0].innerHTML = "changed: "+txt;

}

function nodeSelected(node, e) {
var value = node.get_selected();
if (value) {
//node.setText("OK");
var txt = node.getText();
var e = document.getElementsByName("custom");
e[0].innerHTML = "selected: " + txt;
}
}
 
function myContextMenu(tree, e) {
var cm = e.menuElement;
var node = e.node;
var text = node.getText();
if (node.isFirst()) {
e.set_cancel(true);
alert("no menu for the first item!");
}
if (text == "3.3.1")
  cm.style.backgroundColor = 
     "Red"; else cm.style.backgroundColor = "";
}
</script>
</body>
</html>

自定义样式

与使用内联样式来渲染表格使其看起来像树的 ASP.NET TreeView 不同,OdcTreeView 使用 <ul><li> 标签来原生表示树视图,并完全依赖样式表来控制外观。默认情况下,OdcTreeView 使用“odcTreeView”作为基础样式类,该类作为嵌入资源包含,并包含渲染所需的所有样式,包括要附加的背景图像,如展开和折叠按钮。如果您想自定义样式,只需将源代码中的 TreeView.css 复制出来,将所有 .odcTreeView 出现的地方重命名为您自己的类名,将您的样式表添加到页面,并将 OdcTreeViewClassName 属性设置为该名称。为了使 HTML 代码更短,OdcTreeView 为子元素的类名使用了非常简短的名称,例如 class="ul"。您可能会认为这会导致与其他可能决定使用相同简短名称的控件发生冲突,但由于样式表始终将基础类与子类结合使用,因此这种风险已被消除

.odcTreeView
{
    clear:both;
    vertical-align:top;
    padding:2px;
}


.odcTreeView .ul
{
    list-style-type: none;
    list-style-image: none;
    list-style-position: outside;
    padding: 0px 0px 0px 20px;
    margin:0px;
}

.odcTreeView .Ln
{
    background-image: url('<%=WebResource("Odyssey.Web.TreeView.images.line.gif")%>');
    background-repeat:repeat-y;
    background-position: 0px 0px;
}
…

HTML 代码将如下所示

<div class="odcTreeView" id="OdcTreeView1" EnableClientExpand="true">
<ul class="ul">
<li class="Mid" key="1"><div class="div">
<span class="Collapse" event="collapse"></span>
<span event="click" class="span" id="OdcTreeView1_K1">
<img src="ColorHS.png" style="border-width:0px;" />
<span isText="true">1</span>
…

关注点

源代码包含如何创建 AJAX 服务器控件、如何向控件添加客户端属性和客户端事件、如何从 IHierarchicalDataSource 读取、如何实现 DataTemplate 等等。

历史

这是初始版本。

© . All rights reserved.