树检查列表 ASP.NET Web控件

分享于 

36分钟阅读

Web开发

  繁體 雙語

介绍

TreeCheckList web控件是简单而有效的代码,用于支持分层数据的显示。搜索和选择。 树的外观是非常可以定制的,因为CSS样式支持一切。 控件完全兼容 跨浏览器,并且很容易实现。 可以单独加载数据元素,也可以通过将XML文档分配给控件来。 客户端JavaScript对象类支持所有UI交互,并提供一个帮助查找更大数据集中特定元素的搜索功能。

我开发了这个控件,因为我想方便地选择数据类别,同时还可以在类别中单独选择。 以前通常处理多个列表框,因为大多数树控件不通过父级的父级提供选择关系。 我不想超越设计,或者 hack 一个标准的树控件,因这里我将这个控件放在这个特定的选择中。

演示快速入门

要运行演示,只需下载压缩文件并按以下步骤操作:

  • 创建一个名为TreeCheck的新Web应用程序。
  • 将演示文件解压到 TreeCheck 目录( 如果需要,在项目中包括) 中。
  • 从本地站点(。例如,http://localhost/TreeCheck/TreeDemo.aspx ) 访问 TreeDemo.aspx 文件。

演示提供两个树列表示例,显示如何使用XML数据源和CSS来创建显示。 所有源代码都包含在演示中。

树设计架构

TreeCheckList 是一个非常简单的布局,其中HTML输出只是嵌套的div 元素来表示树结构的级别。 树控件本身是 div 元素,每个节点是由"p"或者"c"表示的div 元素,表示它是父节点或者子节点。 树的HTML输出结构如下所示:

<divid="treeList"style="margin:0; padding:0;"><divid="pNode1_0"><inputid="pNode1_0_chk"type="checkbox"/><span>First Level Node 0</span><divid="i_pNode1_0"style="display:none; visibility:hidden;"><divid="cNode2_0"><span>Second Level Node 0</span></div><divid="cNode2_1"><span>Second Level Node 1</span></div></div></div><divid="pNode1_1"><inputid="pNode1_1_chk"type="checkbox"/><span>First Level Node 1</span><divid="i_pNode1_1"style="display:none; visibility:hidden;"><divid="cNode3_0"><span>Second Level Node 0</span></div><divid="pNode3_1"><inputid="pNode3_1_chk"type="checkbox"/><span>Second Level Node 1</span><divid="i_pNode3_1"style="display:none; visibility:hidden;"><divid="cNode4_0"><span>Third Level Node 0</span></div></div></div><divid="cNode3_2"><span>Second Level Node 2</span></div></div></div><divid="pNode1_2"><inputid="pNode1_2_chk"type="checkbox"/><span>First Level Node 2</span><divid="i_pNode1_2"style="display:none; visibility:hidden;"><divid="cNode5_0"><span>Second Level Node 0</span></div></div></div></div>

在当前版本的代码中,从代码的当前版本中删除了"节点",从而减少了树的HTML大小。

注意,在每个 pNode 中,都有 div 元素" i_pNode"。 这是基于用户展开或者折叠的树级别隐藏或者可见的子节点的组索引元素。 每个节点的文本由 span 元素表示,所有父节点都包含一个checkbox输入元素。

由于树只是一组 divspan 元素,所有元素都可以通过样式轻松控制。 大多数样式信息都由JavaScript代码应用,以便在每个树中下载 LESS 文本。 正如演示所示,每棵树都可以有自己的样式,因这里一个页面上的多个树可以。

服务器代码概述

树控件的基类是 TreeCheckList ( TreeCheckList.cs )。 这是呈现树 div 元素结构并创建客户端JavaScript对象参考和所有CSS和图像参数的代码。 TreeCheckList 类的public 属性用于设置所有显示参数和样式:

TreeListCssClass

主树 div 元素的CSS类。

TreeListCssStyle

主树 div 元素的CSS样式。

TreeLevelCssClass

每个树级别( 附加级别号)的CSS类前缀。

TreeLevelClassOn

true 如果使用 TreeLevelCssClass,则为默认值 true

ImageCollapseURL

打开父节点( 例如,减号)的图像 URL。

ImageExpandURL

关闭父节点( 例如,加号)的图像 URL。

ImageJoinURL

子节点的图像 URL。

ImageTeeURL

没有子节点的一级节点的图像 URL。

ImageTopCollapseURL

水平一级打开父节点的图像 URL。

ImageTopExpandURL

级别一个顶层关闭父节点的图像 URL。

ImageTopNoExpandURL

顶级子节点的图像 URL。

ExpandTreeList

true 如果树最初显示为展开。 默认为 false

NodeImagePosition

TreeCheckListImagePosition enum 设置以定位节点图像。

IndentOn

当前未实现。

ParentNodeLabelCssClass

普通父标签的CSS类。

ParentNodeLabelCssStyle

普通父标签的CSS样式。

ParentNodeSelectedCssClass

选定父标签的CSS类。

ParentNodeMouseOverNormalCssClass

用于普通父标签的CSS类。

ParentNodeMouseOverSelectedCssClass

用于选定父标签的CSS类。

ParentNodeSearchMouseOverNormalCssClass

CSS类的鼠标搜索结果正常父标签。

ParentNodeSearchMouseOverSelectedCssClass

用于搜索结果的CSS类选择父标签。

ParentNodeSearchNormalCssClass

搜索结果的CSS类普通父标签。

ParentNodeSearchSelectedCssClass

用于搜索结果的CSS类的父标签。

ChildLeftIndent

以像素为单位指定子级缩进 margin。

ChildNodeLabelCssClass

普通子标签的CSS类。

ChildNodeLabelCssStyle

普通子标签的CSS样式。

ChildNodeSelectedCssClass

选定子标签的CSS类。

ChildNodeMouseOverNormalCssClass

用于普通子标签的CSS类。

ChildNodeMouseOverSelectedCssClass

选定子标签上的CSS类。

ChildNodeSearchMouseOverNormalCssClass

用于搜索结果的CSS类正常子标签。

ChildNodeSearchMouseOverSelectedCssClass

用于鼠标搜索结果的CSS类选择子标签。

ChildNodeSearchNormalCssClass

搜索结果的CSS类普通子标签。

ChildNodeSearchSelectedCssClass

用于搜索结果的CSS类子标签。

下面的属性函数允许在节点级别上进行自定义鼠标处理。 如果需要,MouseClick 函数可以用于为每个节点提供 postback 事件。

ParentNodeMouseClickFunction

在父节点鼠标单击上执行的脚本函数命令。

ParentNodeMouseOutFunction

在父鼠标上执行的脚本函数命令。

ParentNodeMouseOverFunction

在父鼠标上执行的脚本函数命令。

ChildNodeMouseClickFunction

在子节点鼠标单击上执行的脚本函数命令。

ChildNodeMouseOutFunction

在子鼠标上执行的脚本函数命令。

ChildNodeMouseOverFunction

在子鼠标上执行的脚本函数命令。

要向树控件分配数据,目前有三种 public 方法:

publicint AddNode(string pID, string pLabel, string pParent, string imageUrl)

这里方法用于一次添加一个节点。 参数表示唯一节点 ID ;为显示文本;父节点ID表示 pParent ;如果节点位于级别1,则为空字符串,imageUrl 参数表示这里节点显示的图像。 返回值为节点位置,如果节点ID不是唯一的,则返回 -1.

publicvoid AddNodes(DataTable Nodes, string FieldID, 
 string FieldLabel, string pParent)

这里方法使用 DataTable 来填充具有单个父节点的一组节点。 FieldIDFieldLabel 参数指定包含这些值的DataTable 中的列的名称。 pParent 参数表示父节点的节点 ID。 这里方法还不支持为特定实现写入节点的图像,但容易添加一个容纳图像URL的列。

publicint LoadXml(XmlDocument xmlDoc, string attributeName)

这种方法最有可能是最常用和最简单的填充树的方法。 作为 XmlDocument 传递的XML文件可以表示整个树和所有关联的数据。 attributeName 参数用于指示表示节点显示文本的XML每个级别上的属性。 因此,可以使用相同的XML文件来表示数据的两个不同视图。 当使用这里方法加载树时,每个节点的ID都会自动分配到。

TreeCheckList 类内部,树的节点以 ArrayList的形式表示。 ArrayList _nodeIDs 包含每个节点的唯一标识符。 另一个 ArrayList_nodeParent,包含父对象和子对象之间的关系。 另一个 ArrayList 包含每个节点的显示文本和图像。 呈现树时,遍历 ArrayList的元素来构建 div 元素。 这里呈现的主要功能是在递归 WriteNode 方法中完成的。

protectedint WriteNode(HtmlTextWriter output, string nodeID, object node,
 int level, int pos, bool bHasChildren, string pDivID)
{
 int nChildCount = 1;
 string divID = this.ClientID + "_";
 divID += (bHasChildren)? "p" : "c";
 divID += level.ToString() + "_" + pos.ToString();
 output.Write("<div");
 if (_childLeftIndent!= 0)
 {
 string indent = "margin-left:" + (_childLeftIndent*(level-1)).ToString() + "px;";
 output.WriteAttribute("style", indent);
 }
 if (bHasChildren)
 {
 output.WriteAttribute("id", divID);
 output.WriteAttribute("value", nodeID);
 if (_useTreeLevelClass && (_treeLevelCssClass!= ""))
 output.WriteAttribute("class", _treeLevelCssClass + level.ToString());
 output.Write(">");
 if ((level==1) && (pos==0))
 RenderExpandImage(output, divID, _imageTopExpand);
 else RenderExpandImage(output, divID, _imageExpand);
 if (_imagePosition == TreeCheckListImagePosition.ImageBeforeCheck)
 RenderNodeImage(output, (string)_nodeImages[pos]);
 RenderCheckBox(output, divID);
 }
 else {
 output.WriteAttribute("id", divID);
 output.WriteAttribute("value", nodeID);
 if (_useTreeLevelClass && (_treeLevelCssClass!= ""))
 output.WriteAttribute("class", _treeLevelCssClass + level.ToString());
 output.Write(">");
 if (level==1)
 {
 string img = (pos==0)? _imageTopTee : _imageTee;
 if (img!= "")
 {
 output.Write("<img");
 output.WriteAttribute("align", "absmiddle");
 output.WriteAttribute("src", img);
 output.Write("/>");
 }
 }
 elseif (_imageJoin!= "")
 {
 output.Write("<img");
 output.WriteAttribute("align", "absmiddle");
 output.WriteAttribute("src", _imageJoin);
 output.WriteAttribute("border", "0");
 output.Write("/>");
 }
 }
 if (_imagePosition == TreeCheckListImagePosition.ImageAfterCheck)
 RenderNodeImage(output, (string)_nodeImages[pos]);
 output.Write("<span");
 output.WriteAttribute("id", divID + "_lbl");
 if (bHasChildren)
 {
 if (_pnodeLabelStyle!= "")
 output.WriteAttribute("style", _pnodeLabelStyle);
 if (_pnodeMouseOverFn!= "")
 output.WriteAttribute("onmouseover", _pnodeMouseOverFn);
 if (_pnodeMouseOutFn!= "")
 output.WriteAttribute("onmouseout", _pnodeMouseOutFn);
 if (_pnodeMouseClickFn!= "")
 output.WriteAttribute("onclick", _pnodeMouseClickFn);
 }
 else {
 if (_cnodeLabelStyle!= "")
 output.WriteAttribute("style", _cnodeLabelStyle);
 if (_cnodeMouseOverFn!= "")
 output.WriteAttribute("onmouseover", _cnodeMouseOverFn);
 if (_cnodeMouseOutFn!= "")
 output.WriteAttribute("onmouseout", _cnodeMouseOutFn);
 if (_cnodeMouseClickFn!= "")
 output.WriteAttribute("onclick", _cnodeMouseClickFn);
 }
 output.Write(">");
 output.Write((string)node);
 output.Write("</span>");
 if (_imagePosition == TreeCheckListImagePosition.ImageAfterLabel)
 RenderNodeImage(output, (string)_nodeImages[pos]);
 if (bHasChildren)
 {
 output.Write("<div");
 output.WriteAttribute("id", divID + "_i");
 if (!_expandTreeList)
 output.WriteAttribute("style", "display:none; visibility:hidden;");
 output.Write(">");
 for (int i=pos+1; i<_nodeIDs.Count; i++)
 {
 if ((string)_nodeParent[i] == nodeID)
 {
 nChildCount += WriteNode(output, (string)_nodeIDs[i], 
 _nodeLabels[i], level+1, i, 
 _nodeParent.Contains(_nodeIDs[i]), divID);
 }
 }
 output.Write("</div>");
 }
 output.Write("</div>");
 return nChildCount;
}

TreeCheckList 类还编写必要的JavaScript脚本块,并为 PreRender 方法中的特定树创建客户端对象类。 所有的CSS类设置都被传递给子对象类。

protectedstring ClientInitializeScript()
{
 string sId = this.ClientID;
 string sConstruct = string.Format(
 "var tree_obj_{0} = new CheckTree('{0}', '{1}', '{2}', '{3}'," + 
 "'{4}', '{5}', '{6}', '{7}', '{8}', '{9}', '{10}', '{11}'," + 
 "'{12}', '{13}', '{14}', '{15}', '{16}', '{17}', '{18}', '{19}', '{20}');", sId
, _imageTopExpand
, _imageTopCollapse
, _imageExpand
, _imageCollapse
, _pnodeLabelClass
, _pnodeSelectedClass
, _pnodeNormalOverClass
, _pnodeSelectOverClass
, _pnodeSearchNormalClass
, _pnodeSearchSelectClass
, _pnodeSearchNormalOver
, _pnodeSearchSelectOver
, _cnodeLabelClass
, _cnodeSelectedClass
, _cnodeOverNormalClass
, _cnodeOverSelectClass
, _cnodeSearchNormalClass
, _cnodeSearchSelectClass
, _cnodeSearchNormalOver
, _cnodeSearchSelectOver
 );
 returnstring.Format(GetScriptTemplate(), sConstruct);
}

使用TreeCheckList类

在我的演示代码中,我创建了两个树控件示例。 我在它的他 WebControl 对象中使用 TreeCheckList 控件类来提供更多的绝缘和抽象,还显示了如何创建自定义的。

TreeListControl1.ascx 和 TreeListControl2.ascx 是两个嵌入 TreeCheckList 控件的用户控件。 检查这些控件的基本 HTML,表明它们是非常简单的容器:

<%@ControlLanguage="c#"AutoEventWireup="false"Codebehind="TreeListControl2.ascx.cs"Inherits="TreeCheck.TreeListControl2"TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%><divid="Tree2"><divid="TreeList2"><divid="TreeCtrl2"><divid="Tree2Top"><divid="Tree2Header"></div><divclass="treeSearch2"id="TreeSearch2"runat="server"></div></div><divid="TreeArea2"runat="server"class="TreeArea2"></div><divclass="treeInfo2"id="TreeInfo2"runat="server"></div></div></div></div>

代码的简单性是明显的,因为在这里添加了树控件,以及提供搜索界面的几个控件:

privatevoid Page_Load(object sender, System.EventArgs e)
{
 /* Create a new tree and set properties */ TreeCheckList tcl = new TreeCheckList();
 tcl.ImageTopExpandURL = "tree_arrow_expand2.gif";
 tcl.ImageTopCollapseURL = "tree_arrow_collapse2.gif";
 tcl.ImageExpandURL = "tree_arrow_expand2.gif";
 tcl.ImageCollapseURL = "tree_arrow_collapse2.gif";
 tcl.ImageJoinURL = "tree_join.gif";
 tcl.ImageTopNoExpandURL = "";
 tcl.ImageTeeURL = "";
 tcl.NodeImagePosition = TreeCheckListImagePosition.ImageAfterCheck;
 tcl.TreeListCssClass = "T2_tree";
 tcl.TreeLevelCssClass = "T2_level";
 tcl.ParentNodeLabelCssClass = "T2_treeParentLabel";
 tcl.ParentNodeSelectedCssClass = "T2_treeParentSel";
 tcl.ParentNodeMouseOverNormalCssClass = "T2_treeParentNormalOver";
 tcl.ParentNodeMouseOverSelectedCssClass = "T2_treeParentSelectOver";
 tcl.ParentNodeSearchNormalCssClass = "T2_treeParentSearchNormal";
 tcl.ParentNodeSearchSelectedCssClass = "T2_treeParentSearchSelect";
 tcl.ParentNodeSearchMouseOverNormalCssClass = "T2_treeParentSearchNormalOver";
 tcl.ParentNodeSearchMouseOverSelectedCssClass = "T2_treeParentSearchSelectOver";
 tcl.ChildNodeLabelCssClass = "T2_treeChildLabel";
 tcl.ChildNodeSelectedCssClass = "T2_treeChildSel";
 tcl.ChildNodeMouseOverNormalCssClass = "T2_treeChildMouseOverNormal";
 tcl.ChildNodeMouseOverSelectedCssClass = "T2_treeChildMouseOverSelect";
 tcl.ChildNodeSearchNormalCssClass = "T2_treeChildSearchNormal";
 tcl.ChildNodeSearchSelectedCssClass = "T2_treeChildSearchSelect";
 tcl.ChildNodeSearchMouseOverNormalCssClass = "T2_treeChildSearchNormalOver";
 tcl.ChildNodeSearchMouseOverSelectedCssClass = "T2_treeChildSearchSelectOver";
 tcl.ChildLeftIndent = 0;
 /* Load the data into the tree from XML file */ tcl.LoadXml(xmlDoc, xmlAttribute);
 /* Add the tree to this control */string msg = "";
 try {
 TreeArea2.Controls.Add(tcl);
 }
 catch(Exception ex)
 {
 msg = ex.Message;
 }
 // This is specific code added to provide a display change on tree// selection in the javascript for this example. It is not needed // when using this control outside of this demo.string jsTemp = 
 "<script language="'javascript'"> tmpSelectionDisplayDivs.put('" + 
 tcl.ClientID + "', 'TreeSelections2'); </script>";
 Page.RegisterClientScriptBlock("TreeListC2", jsTemp);
 /* Setup search buttons and textbox and add to control */ TextBox txtSearch = new TextBox();
 txtSearch.ID = tcl.ClientID + "txtSearch";
 txtSearch.CssClass = "T2_searchText";
 txtSearch.EnableViewState = false;
 txtSearch.MaxLength = 50;
 txtSearch.AutoPostBack = false;
 TreeSearch2.Controls.Add(txtSearch);
 string btnImageGo = "btn_small_search.gif";
 string btnImageHv = "btn_small_search_hvr.gif";
 System.Web.UI.WebControls.Image btnImage = 
 new System.Web.UI.WebControls.Image();
 btnImage.CssClass = "searchButton";
 btnImage.ImageUrl = btnImageGo;
 btnImage.ImageAlign = ImageAlign.AbsMiddle;
 btnImage.Attributes.Add("onmouseover", "this.src='" + 
 btnImageHv + "'; style.cursor='pointer';");
 btnImage.Attributes.Add("onmouseout", "this.src='" + 
 btnImageGo + "'; style.cursor='default';");
 HtmlAnchor btnSearch = new HtmlAnchor();
 btnSearch.ID = tcl.ClientID + "btnSearch";
 btnSearch.Controls.Add(btnImage);
 TreeSearch2.Controls.Add(btnSearch);
 string imgNext = "btn_rightarrow_search.gif";
 string imgNextHvr = "btn_rightarrow_search.gif";
 System.Web.UI.WebControls.Image btnImgNext = 
 new System.Web.UI.WebControls.Image();
 btnImgNext.CssClass = "searchNext";
 btnImgNext.ImageUrl = imgNext;
 btnImgNext.ToolTip = "Find Next";
 btnImgNext.ImageAlign = ImageAlign.AbsMiddle;
 btnImgNext.Attributes.Add("onmouseover", "this.src='" + 
 imgNextHvr + "'; style.cursor='pointer';");
 btnImgNext.Attributes.Add("onmouseout", "this.src='" + 
 imgNext + "'; style.cursor='default';");
 HtmlAnchor btnNext = new HtmlAnchor();
 btnNext.ID = tcl.ClientID + "btnNext";
 btnNext.Attributes.CssStyle.Add("visibility", "hidden");
 btnNext.Controls.Add(btnImgNext);
 TreeSearch2.Controls.Add(btnNext);
 /* Assign javascript search function events to buttons */ btnSearch.Attributes.Add("onclick", 
 "event.cancelBubble = true; variableSearch(event, '" + 
 tcl.ClientID + "', '" + txtSearch.ClientID + "', '" + 
 btnSearch.ClientID + "', '" + btnNext.ClientID + "', '" + 
 TreeInfo2.ClientID + "');");
 btnNext.Attributes.Add("onclick", 
 "event.cancelBubble = true; variableNext(event, '" + 
 tcl.ClientID + "', '" + txtSearch.ClientID + "', '" + 
 btnSearch.ClientID + "', '" + btnNext.ClientID + 
 "', '" + TreeInfo2.ClientID + "');");
}

唯一要注意的是 Page_Load 方法的最后两行。 这些是调用的JavaScript函数,用于在客户端上对树控件数据执行搜索。 functions函数包含在JavaScript文件 TreeSearch.js 中,后者被添加到主插件的file文件的head 部分。 TreeSearch.js 文件也可以在 TreeListControl1 或者 TreeListControl2 代码中注册。

客户端JavaScript代码和CSS概述

重要笔记: 本演示包括对 browser_detect.js 和 utility.js的参考。 这些是我总是在使用JavaScript的每个项目中包含的全局文件。 在项目中,不要忘记将文件放到页面头中,也不要在代码中包含它们,或者在代码中使用它们。

一旦 TreeCheckList 控件被呈现,它将显示在客户端浏览器中,它的中 CheckTree JavaScript对象类将接受。 CheckTree 类支持操作树的显示。捕获选择更改以及提供搜索树内容的能力的各种功能。 初始化树时,CheckTree 将事件监听器添加到每个节点以提供鼠标和复选框单击处理。

处理父复选框单击:

var chks = this.obj.getElementsByTagName("input");for (var i=0; i<chks.length; i++)
{
 chks[i].checked = false;
 if (this.IsParentNode(chks[i].id))
 {
 if (chks[i].attachEvent)
 {
 chks[i].attachEvent('onclick', 
 function(e) { eval("NodeChecked(e,'" + id + "',true);") });
 chks[i].attachEvent('onmouseover', 
 function(e) { eval("ChkMouseOver(e,'" + id + "');") });
 chks[i].attachEvent('onmouseout', 
 function(e) { eval("ChkMouseOut(e,'" + id + "');") });
 }
 elseif (chks[i].addEventListener)
 {
 chks[i].addEventListener('click', 
 function(e) { eval("NodeChecked(e,'" + id + "',true);") }, false); 
 chks[i].addEventListener('mouseover', 
 function(e) { eval("ChkMouseOver(e,'" + id + "');") }, false); 
 chks[i].addEventListener('mouseout', 
 function(e) { eval("ChkMouseOut(e,'" + id + "');") }, false); 
 } 
 }
}

实例化树时,CheckTree 对象被添加到树的全局哈希表中,以便每个页面都可以使用多个树。

TREECHECK_Trees.put(this.id, this);

随着树上的选择更改,CheckTree 函数处理事件并使用所需的CSS类指定更新节点。 这个CSS类的赋值是在单个函数 SetNodeClass 中完成的:

function CheckTree_SetNodeClass(nodeID, bMouseOver)
{
 var nodeObj = getObject(nodeID);
 if (!nodeObj)
 return;
 if (this.IsParentNode(nodeID))
 {
 if (this.IsNodeSelected(nodeID) || this.HasSelections(nodeID))
 {
 if (this.searchID == nodeID)
 this.SetNodeTextClass(nodeID, 
 (bMouseOver)? this.parentSearchSelectedOverCss : 
 this.parentSearchSelectedCss);
 elsethis.SetNodeTextClass(nodeID, (bMouseOver)? 
 this.parentSelectedOverCss : this.parentSelectedCss);
 }
 else {
 if (this.searchID == nodeID)
 this.SetNodeTextClass(nodeID, 
 (bMouseOver)? this.parentSearchNormalOverCss : 
 this.parentSearchNormalCss);
 elsethis.SetNodeTextClass(nodeID, (bMouseOver)? 
 this.parentNormalOverCss : this.parentNormalCss);
 }
 }
 elseif (this.IsChildNode(nodeID))
 {
 if (this.IsNodeSelected(nodeID))
 {
 if (this.searchID == nodeID)
 this.SetNodeTextClass(nodeID, 
 (bMouseOver)? this.childSearchSelectedOverCss : 
 this.childSearchSelectedCss);
 elsethis.SetNodeTextClass(nodeID, (bMouseOver)? 
 this.childSelectedOverCss : this.childSelectedCss);
 }
 else {
 if (this.searchID == nodeID)
 this.SetNodeTextClass(nodeID, 
 (bMouseOver)? this.childSearchNormalOverCss : 
 this.childSearchNormalCss);
 elsethis.SetNodeTextClass(nodeID, (bMouseOver)? 
 this.childNormalOverCss : this.childNormalCss);
 }
 }
}

单击父复选框或者选择单个子对象时,将遍历父子链,并更新所有相关节点以反映新选择。 每个选定节点都在 nodesSelected 哈希表中捕获,它也可以用于提供更新的信息到服务器。

CheckTree 对象类提供了基本搜索函数,用于在树中查找节点文本,然后将找到的节点滚动到视图中。 为了操纵这些搜索功能并向用户提供反馈,你可能需要添加其他JavaScript函数来调用搜索功能。 我已经在 TreeSearch.js 中提供了一组功能。 函数 variableSearch 启动初始搜索,并使用超时调用为用户提供一个优秀的等待 cursor。 函数 variableNext 对继续符合搜索条件的下一个项执行相同的任务。 这些函数被分配给 TreeListControl1TreeListControl2 类中的按钮。

JavaScript代码的一个重要特性应该突出显示,即在选中或者关闭复选框时触发的事件函数。 因为大树中的父节点可以能有数百个子节点,所有这些节点更新时可能会有显著的用户等待。 我想向用户展示树正忙,所以我将 NodeChecked 函数分成两部分。 函数的第一部分验证参数并设置节点的选中状态。 然后函数禁用父节点并调用 setTimeout 来处理子节点的其余部分。 对 setTimeout的调用确保显示等待 cursor 并且节点显示为用户的活动状态。

function CheckTree_NodeChecked(nodeID, bUpdate)
{
 if (!this.IsParentNode(nodeID))
 return;
 var nodeObj = getObject(nodeID);
 if (!nodeObj)
 return;
 var inodeID = nodeID + "_i";
 var inodeObj = getObject(inodeID);
 if (!inodeObj)
 {
 alert('no object: ' + inodeID);
 return;
 }
 var chkObj = getObject(nodeID + "_chk");
 if (chkObj)
 {
 this.SetNode(nodeID, chkObj.checked);
 cursor_wait();
 nodeObj.disabled = true;
 setTimeout("CheckTree_NodeChecked_OnTimer('" + this.id + "', '" + 
 nodeID + "'," + chkObj.checked + "," + bUpdate + ")", 5);
 }
}

计时器触发时,NodeChecked 函数的下半部分被调用。 这个函数的这一部分实际上遍历了所有的子节点和父节点,设置选择状态。 当函数结束时,它将父节点恢复到启用状态,并清除等待光标。

注意:如果只包含少量数据,那么 NodeChecked 函数可以合并成一个函数,而不需要 setTimeout。 这将提供更快的选择和更好的用户交互。

这个演示的所有样式都包含在文件 TreeCheck.css 中。 我试图保持设置相当简单,但不同的是创建两个不同的树显示。 文件顶部部分的设置用于TreeDemo页面布局。 单独的树设置是在。 查看Tree2设置,可以看到这个树使用树级类为树中的每个子级提供特定的样式。 在服务器代码中,树级别类被指定为"t2_level"。 然后,水平数字被附加到类名中,在CSS文件中,必须提供每个类级别的T2_level1.T2_level2.等等,使用这些级别类,可以显著地改变树的外观。

CSS文件中提供了树节点每个状态的文本标签类。 这些不同的状态可以像一个样式一样简单,或者随着每个节点状态的变化而急剧变化。 在演示中,Tree2子节点在选定时从蓝色变为 yellow,并且 background 也改变颜色。

.T2_treeChildLabel{
 color: Blue;font-weight: normal;margin-left: 5px;}
.T2_treeChildSel{
 color: Yellow;font-weight: normal;background-color: #8F8FAF;margin-left: 5px;padding-left: 2px;padding-right: 2px;}

当鼠标在子节点上时,注意左边的margin 稍微增加,以提供"浮点数"效果。

.T2_treeChildMouseOverNormal{
 color: #303082;font-weight: normal;cursor: pointer;background-color: #ACDEFF;margin-left: 8px;}

这里外,当子节点作为搜索结果发现时,会添加一个边框来给用户一个非常好的视觉线索。

.T2_treeChildSearchNormalOver{
 background-color: #EFEFBF;
 color: Blue;
 font-weight: normal;
 border: solid 1px #8F8FAF;
 margin-left: 8px;
 padding-left: 2px;
 padding-right: 2px;
 cursor: pointer;
}

在树上使用样式的无限组合可以给其他常见控件提供一些非常令人印象深刻和不同的外观。

限制和未来的增强

这里最重要的限制是将选择返回到服务器的方式。 控件的个人实现使用特定的AJAX回调回服务器,这样我就可以在没有 postback的情况下更新页面上的其他控件。 我不想用许多只针对我的需求的AJAX代码来减轻这个示例,因这里我将 JavaScript SelectionsChanged 函数放在。 换句话说,这个版本的控件中没有 postback 处理。 你必须实现一些东西来满足你的需求。 演示中显示了页面上另一个文本控件如何使用树控件选项进行更新。 由于还有分配给每个节点的值,可以检索并发送回服务器,或者用JavaScript更新页面。 我希望将来添加一个 postback 事件处理程序,但是我不需要这样做,它不是一个高优先级。 如果你决定实现某些内容,请共享。

此外,我没有向这里控件添加视图视图。 我没有任何原因来保存全部状态,因为我不希望将页面回发到服务器,并通过AJAX进行更新。 树的状态可以使用JavaScript函数 LoadSelectionState 来恢复。 这个函数接受 nodeID的一个 array,并从这个 array 恢复树的选择状态。 如果在树中合并 postback,则使用这里函数通过注册服务器代码中的JavaScript调用来恢复选择。 我将在某些时候添加 viewstate。

我必须道歉的一件事是JavaScript中缺少注释。 我保证我会提供更多关于参数和代码的信息。 : )

确认

我想感谢 Mike Ellison的DropDownCheckList 控制( DropDownCheckList.aspx )。 我在查看他的控制之后想到了 TreeCheckList的想法。 mike和代码的控制是一流的,我尽力模仿这种类型的开发。


WEB  控制  asp  asp-net  lis  列表  
相关文章