TreeView穿新衣: 以优雅的名义
[引]
Microsoft.Web.UI.WebControls.TreeView是IE WebControl系列组件之一. 可以以客户端脚本的形式显示一个可折叠/展开的分层次树型目录, 本文简单描述了如何在TreeView的基础上进行改造, 使之满足某些环境下的应用, 改造的方法是保持TreeView完整和性能的前提下在其外围上增加一个Wrapper, 使之可满足某些输出需求.
[为何需要改造]?
在我第一次接触IE Web Control系列控件时, 对我吸引力最大的还是TreeView. 也为此兴奋了一段时间, 但是后来很快我就发现, 这个东西有点中看不中用. 我曾经费劲心思地寻找发回到服务器端的节点的标识符, 结果发现在服务器端我仅能寻找到节点的Index值. 这个Index值对我来说是毫无意义的. 比如, 当我的树型显示我国的行政区划数据时, 我单击了“河北”节点, 这个数据被发送到服务器, 我期望得到的数据时河北的标识符, 但是我却仅仅能得到类似0.0.0.1这样的Index值. 这样TreeView就成了鸡肋, 无所用, 不用又可惜.
[解决的方法]
开始的想法是直接改造前端的脚本资源文件(HTC). 后来觉得不妥. TreeView虽然未提供技术支持, 但是也是在不断升级中. 而且也没有许多精力研究那个冗长的脚本文件. 并且修改已经成型的发布的代码并不是 于是我仅仅留意了一下HTC公开的一些方法和特性. 虽然在服务器端只能按照Index识别节点, 但是在数据文件内部, 每个节点却包含一个NodeData Attribute. 在服务器端向树型增加节点的时候, 可以设置该节点的NodeData特性. 并且在客户端, 可以由getAttribute("NodeData")方法得到该数据. 这样数据的写入, 读出, 保持环节都没有问题. 缺的就是一个包装的过程.
[思路]
要在服务器端得到NodeData我们必须自己实现前端数据到后端的POST过程. 这一过程需要写一段简短的脚本来实现. 脚本捕获客户端树型对象的onselectedindexchange事件(选中的节点更改, 这一事件通常在用户单击节点时触发)脚本的主要过程是:
1.寻找到当前被单击的节点
2.取得该节点的NodeData数据
3.将该数据POST到后端.
4.后端代码以一定逻辑解释该数据, 并写入到SelectedNodeData公共特性.
[实施]
ASCX设计:
在IDE环境中建立一个新的ASCX. HTML代码部分增加一个Panel服务器控件. Panel中仅增加一个TreeViewb服务器端标记. <IE:TreeView ID=MainTree Runat=Server />
后端环境准备阶段:
在ASCX初始化的过程中除了设置树型的数据外, 在Panel控件的最末尾使用Controls集合的Add方法向容器中增加一个Hidden类型的HTML Form Input Element. 并且该Input Element的name设置为ASCX的UniqeID. 这个HIdden域负责隐式的将数据Post到后端.
前端事件控制
必须对用户的单击操作作出响应. 我们在前台找到TreeView的Reference后, 捕获其selectindexchange事件(将事件句柄设置到前端的function上). 接下来首先寻找到当前被单击的节点. 之后取到该NodeData. 将NodeData值以一定逻辑设置到hidden域的Value上. 之后将客户端Form Submit上去.
代码示例:
document.getElementById("__tree_container").all.tags("treeview")[0].onselectedindexchange = function(){
var source = event.srcElement;
var hidden = document.getElementById("__tree_container").all.tags("INPUT")[0];
var node = source.getTreeNode(source.selectedNodeIndex).getAttribute("NodeData");
var index = source.selectedNodeIndex;
if(node != null){ if(node.length>0){
if(node.indexOf(":") != -1){
hidden.value = node+ ':' + index;
document.forms[0].submit();
}
}
}
后端回发事件处理
要是控件可以处理回发事件. 必须使控件实现IPostBackDataHandler接口. 在必须实现的LoadPostData方法中, 可以获得发回到后端的NodeData值. 以既定逻辑分析该值, 将该值公开到后端的SelectedNodeData特性上. 这个过程也可以引发一个事件.
[方法与特性公开]
需要引起注意的是. 在实施了本方案后, 原先TreeView的公共特性和方法在ASCX之外将变得不在可见. 这样我们在无法在ASCX之外直接驱动该TreeView. 也就是无法向树型中写入数据. 如果我们要向应用包装前的TreeView那样应用穿了新衣的ASCX, 就必须将原有API全部再次公开到ASCX上. 这个有一定的工作量. 也是满足内聚性要求所必须的. 但是一般而言. 在一个项目中我们也不可能使用多种多样的方法写入树型的数据. 这就是一个权衡的过程. 我们可以选择我们最熟悉最需要的方法公开. 一直到我们可以承受的工作量.
另一方面, 如果我们的树型里是一些固定数据. 也就是在ASCX外一般不会直接操作该数据的话, 我们完全可以把数据的设置过程在ASCX内部进行. 这样, 我们不用公开任何TreeView的特定API.
[应用]
[补充说明]
以上处理过程仅仅是在现有API上包装(Wrapper), 而不是改造.
除了NodeData外, 可以改造数据分析和处理的逻辑, 将其他数据也同时发送到后端处理.
关于”处理回发数据”, 可参考MSDN文档中索引”处理回发数据”, 或参考IPostBackDataHandler接口概述.
本文是基于较老的TreeView版本, 可能当前发布的新版本TreeView已经提供该API. 这样就无需如此繁琐.