cuike519的blog

工作、学习、娱乐

用户操作
[即时聊天] [发私信] [加为好友]
wujianID:cuike519
86769次访问,排名1171(1),好友2人,关注者13人。
cuike519的文章
原创 44 篇
翻译 0 篇
转载 0 篇
评论 133 篇
cuike519的公告
很久没有写blog了,总觉得生活少点什么。我的新邮件Mathew.mike@hotmail.com My MSNspase is http://gunsbypower.spaces.msn.com/
最近评论
nychanglingfeng:真是挺牛的
nychanglingfeng:真是挺牛的
fmmmeck:今天我也遇到这个问题了,多亏了这篇文章和 lf_shao 的修正 !
fmmmeck:今天我也遇到这个问题了,多亏了这篇文章和 lf_shao 的修正 !
fmmmeck:今天我也遇到这个问题了,多亏了这篇文章和 lf_shao 的修正 !
文章分类
收藏
    相册
    程序代码
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 TreeView父子联动效果保持节点状态一致收藏

    新一篇: 如何使CheckBoxList的Attributes属性生效(修改微软的一个bug) | 旧一篇: 如何有效的使用C#读取文件

            我们大部分都用过TreeView控件,对这个控件的评价也是各式各样的,但是我觉得不论如何它是一个免费的开源的控件,所以我还是在用它。在刚接触ASP.NET的时候,记得需要做一个分配权限的权限树,当时只知道有这个树,经过一天的研究对其服务器端的行为基本以及搞清楚了,但是由于当时的js水平有限,所以对客户端的代码很畏惧,基本没有看过。
    当时有这样一个要求:如果一个节点被选中则该节点的所有子节点都要选中,如果该节点的所有子节点取消选择则该节点也要取消选择(节点一致性),相反一样。
    还有一个要求就是:如果可以实现三态树则更好(这个有点难,本文中不予讨论)。本文将详细介绍前面要求1。
            首先我们要庆幸微软的这个TreeView控件是开源的,不论是客户端代码还是服务器端代码您都可以轻松获得,可以去微软的网站上下载。在网上也见过一些讲述该问题的文章,他们大多是使用一个TreeView之外的js实现,我个人认为从面向对象的观点,该功能属于TreeView,所以没有理由将它分离出去,因此今天我将修改TreeView.htc来实现上面的功能。要想获得该文件(TreeView.htc)可以去微软的网站上下载之。
    说实在的该功能曾经也困扰了我很久,一直想解决这个问题,但是一直没有时间来研究TreeView.htc中的代码,今天终于下决心搞定它。
            我们知道在TreeView的oncheck中需要激发该事件处理函数,这个函数很容易找到,可以在TreeView生成的客户度脚本中找到,代码片断如下:
    oncheck="javascript: if (this.clickedNodeIndex != null) this.queueEvent('oncheck', this.clickedNodeIndex)"
            由此我们可以去htc(以后说的htc都指的是TreeView.htc),找到doCheckboxClick方法,只要看名字就知道它是干什么的(命名实在太重要了,否则在3000行代码中找某个函数真的很累)。
            该方法是当用户点击CheckBox的使用要处理的函数,函数如下所示:
            function doCheckboxClick(el){
                var checked = private_getAttribute(el,"checked")
                el.checked = !checked;
                var evt = createEventObject();
                evt.treeNodeIndex = getNodeIndex(el);
                g_nodeClicked = el;
                _tvevtCheck.fire(evt);
                setNodeState(el,el.checked);// maybe need el only,but I think it's safly
            }
            其中第一行、第二行以及最后一行是我添加的,第一二行是为了在页面回传以后checked属性无效时所做的修改,按照原来的方法在提交后el.checked是undefined,所以导致无法正确同步节点,如果读者有兴趣可以试一试。setNodeState函数,从名字上可以看出该函数是用来设置节点状态的,它将当前你选择的节点作为参数传递到函数内部。如注释中所说实际上你可以只传递el进去,而无需再传一个布尔值,不过我想这样传递可能更安全,不过没有关系,你觉得不爽可以修改:。
            下面看看setNodeState的函数体,该函数由两个方法组成,一个是设置所有孩子节点的状态,一个是设置该节点的父节点状态。代码如下:
            function  setNodeState(el,state){
                _setChildNode(el,state);
                _setParentNode(el,state);
            }
            为了可以设置当前节点的所有孩子节点是否和父节点(本节点)状态一至我们需要函数_setChildNode,同理为了使节点的父节点和该节点状态一至我们需要_setParentNode函数。两个函数都是递归函数,将会遍历所有的子节点和所有的父节点以及兄弟节点(为什么要遍历兄弟节点稍候再说),下面我们先看看遍历孩子节点的代码,代码如下所示:
            function  _setChildNode(el,state){
                var childNodes = el.children;
                if(childNodes.length > 0){// if has childs 
                    for(var i = 0 ;i<=childNodes.length-1;i++){
                        private_setAttribute(childNodes[i],"Checked",state);
                        _saveCheckState(childNodes[i]);
                        _setChildNode(childNodes[i],state);
                    }
                }
            }
            该函数先获得当前节点的所有子节点,这里可以直接使用TreeView提供的函数getChildren方法,而我使用的使html的原始方法。如果该节点存在子节点则遍历所有子节点,同时设置子节点的状态和当前节点的状态一致,_saveCheckState函数的作用稍候会介绍。private_setAttribute方法为TreeView提供的一个内部设置属性的方法(js没有private关键字,这里只是做说明而已),该方法将设置每一个节点的Checked属性为当前这个节点的状态。
            下面是如何设置父节点状态的代码:
             function  _setParentNode(el,state){
                var parentNode = el.parentElement;
                if(parentNode){
                    if(!_checkSiblingdNode(el)){
                        private_setAttribute(parentNode,"Checked",state);
                        _saveCheckState(parentNode);
                        _setParentNode(parentNode,state);
                    }
                }
            }
            该函数先获得其父节点,如果父节点存在则检查当前节点的兄弟节点,上面有提到了需要检查兄弟节点,这里检查兄弟节点的目的是:父节点的状态不是由当前节点一个节点控制的(这种情况只存在于当前节点没有兄弟节点的情况下),如果有其他兄弟是选中的,那么父节点是不能被取消了,这样将导致兄弟节点和父子节点不一致不一致。该函数里面也调用了_saveCheckState函数,在后面将介绍之。
    下面的代码描述了如何检查兄弟节点的状态,代码如下:
            function _checkSiblingdNode(el){
                var parentNode = el.parentElement;// you can use getParentTreeNode function in this htc,but I like this usage.
                for(var i = 0;i<=parentNode.children.length-1;i++){
                    if(el != parentNode.children[i]){
                        if(private_getAttribute(parentNode.children[i],"Checked")){
                            return true;
                        }
                    }
                }
                return false;
            }
            正如注释所述,你可以使用getParentTreeNode方法来获得节点的父节点,但是我比较喜欢使用原始的方法:。这里将排除自己(if(el!=parentNode.children[i]))。
            有了上面的代码我们就可以做到客户端无刷新的父子节点一致的选择,那么我们现在来介绍一下_saveCheckState函数,如果没有该函数页面提交以后,刚才除了手工点击的节点以外其他的节点都不能保存状态。所以该函数的作用就是要保存所有节点的状态(主要是自动选择的节点),保证在回传之后树的状态依然存在。下面的代码描述了_saveCheckState函数,代码如下:
            function _saveCheckState(el){
                if(getNodeIndex(el))
                    queueEvent('oncheck', getNodeIndex(el));
            }
            该方法首先检查当前节点的index是否有效,如果有效则保存状态。这里需要说一下queueEvent方法做了什么,代码我就不帖了,该函数的实际意义是将客户端的操作保存在一个名为__TreeView1_State__的隐藏域中,这样在回传的时候服务器端会根据该隐藏域,初始化树的状态。其中TreeView1是我在测试系统中TreeView的名字。
            最后的问题就是部署的问题了,因为修改了htc,所以在部署的时候需要替换原来的htc文件。
            如果有需要修改之后的TreeView.htc(格式化之后的),可以在CSDN上改我留言留下email。我的CSDN帐号是cuike519。
            希望微软可以早日将这个功能添加到TreeView控件中,最好也能实现多态的树结构。请浏览blog的相关评论,我会在评论中更新文章!

    发表于 @ 2005年02月02日 18:01:00|评论(loading...)|编辑

    新一篇: 如何使CheckBoxList的Attributes属性生效(修改微软的一个bug) | 旧一篇: 如何有效的使用C#读取文件

    评论

    #jetz 发表于2005-05-27 00:38:00  IP:
    TrackBack来自《treeview,js》

    TrackBack From:http://www.cnblogs.com/jetz/archive/2005/05/27/163284.html
    #jetz 发表于2005-06-15 09:38:00  IP:
    TrackBack来自《treeview,js》

    TrackBack From:http://www.cnblogs.com/jetz/archive/0001/01/01/163284.html
    #gmmylose 发表于2005-08-05 17:31:00  IP:
    TrackBack来自《Micrisoft TreeView》

    TrackBack From:http://gmmylose.cnblogs.com/archive/2005/08/05/208461.html
    #wei 发表于2005-03-22 18:25:00  IP: 61.154.12.*
    你好,我正好需要一份这样功能的.

    能发一份到我邮箱里么?谢谢.

    wlh@uptop.cn
    #wei 发表于2005-03-30 17:44:00  IP: 61.154.12.*
    楼主,不知你什么时候会来你的BLOG.
    我向你要这份HTC要了好久了,也没见你反应.在CSDN也向你发短信了,后来因为我急需,照你上面的代码改了下HTC文件.

    现在出现了个问题:
    假如树首下有几个同级的树,这些树下分别有好多子树,这时,如果点其中的一个一级树,第一次可能会选中下面的子树,但之后好像会选不中了..
    但如果总共就一个大类,就不会有这问题.
    不知你有没有碰到过?

    wlh@uptop.cn
    #rh800131 发表于2005-08-21 13:10:00  IP: 211.100.4.*
    我想要一份修改过的.HTC文件,能给我一份么,我只想实现点击节点时,页而不会每次都刷新的效果,无刷新的方法,我在网上只找到用javascript写的,那个好复杂,看不怎么明白,谢谢,我的邮箱是...taiji_liqing780515@sina.com
    #eric 发表于2005-08-23 12:42:00  IP: 211.100.4.*
    我也想要一份,谢谢
    mlzboy@yahoo.com.cn
    #22 发表于2005-08-25 10:53:00  IP: 211.100.4.*
    厉害
    #cuike519 发表于2005-09-09 23:56:00  IP: 211.100.21.*
    /* 下面这段代码是我经过修改后的,你只需要在TreeView.htc里面找到doCheckboxClick函数并修改这个函数,同时添加这个函数后面添加的所有函数即可(注释开始到结束)。该方法我已经测试,解决了文章中描述的可能出现节点不一致的问题。
    由于没有时间给每位网友发送并且也没有合适的服务器可以上传文件所以只有在这里贴代码了!
    */
    function doCheckboxClick(el){
    if(el.checked != event.srcElement.checked)
    el.checked = event.srcElement.checked;
    else
    el.checked = !el.checked;
    var evt = createEventObject();
    evt.treeNodeIndex = getNodeIndex(el);
    g_nodeClicked = el;
    _tvevtCheck.fire(evt);
    setNodeState(el,el.checked);
    }

    // add by wujian for check
    function _setChildNode(el,state){
    var childNodes = el.children;
    if(childNodes.length > 0){// if has childs
    for(var i = 0 ;i<=childNodes.length-1;i++){
    private_setAttribute(childNodes[i],"checked",state);
    _saveCheckState(childNodes[i]);
    _setChildNode(childNodes[i],state);
    }
    }
    }

    function _setParentNode(el,state){
    var parentNode = el.parentElement;
    if(parentNode){
    if(!_checkSiblingdNode(el)){
    private_setAttribute(parentNode,"checked",state);
    _saveCheckState(parentNode);
    _setParentNode(parentNode,state);
    }
    }
    }

    function setNodeState(el,state){
    _setChildNode(el,state);
    _setParentNode(el,state);
    }

    function _saveCheckState(el){
    if(getNodeIndex(el))
    queueEvent('oncheck', getNodeIndex(el));
    }

    function _checkSiblingdNode(el){
    var pare
    #cuike519 发表于2005-09-09 23:58:00  IP: 211.100.21.*
    上面贴的代码里面所有的checked都是小写的,请大家注意否则会出现奇怪的事情!:-)
    #ITL 发表于2005-09-14 13:43:00  IP: 211.100.21.*
    谢谢,我等了好久了
    #ITL 发表于2005-09-14 14:12:00  IP: 211.100.21.*
    我的树出了个问题,只要在_setChildNode()函数中加上“保存已选择点”的函数后就会出现死循环(_saveCheckState())。而在_setParentNode()函数中是好的。我看了一下问题出在getNodeIndex()这个函数里。
    在这个函数中的while (col[i] != node) i++;出现死循环。但我没搞过htc的编程,所以搞不来。希望能够帮我一下,谢谢!
    #ITL 发表于2005-09-14 14:22:00  IP: 211.100.21.*
    我的E-mail:itlzjrangel@163.com
    QQ:22114891
    Msn:itlzjrangel@hotmail.com
    #pitt 发表于2005-09-14 17:00:00  IP: 211.100.21.*
    还是出现节点不一致的情况啊:
    点父结点时会将子结点的状态变反. 也就是说:在点这个父结点前,如果其下的子结点一部分是选中的,一部分是未选中的,那么点父结点全选之后,表面上看起来其子结点全选中了,但在保存记录时在CS代码中去读其下属子结点的状态时,发现正好交换了一下,原来选中的变成了未选中,未选中的变成了选中.
    能解决吗?
    #csterry 发表于2005-10-12 11:09:00  IP: 211.100.21.*
    好像会出现cs代码中取得的值和界面显示的选中项不一致的情况
    #freeman 发表于2005-10-26 14:00:00  IP: 211.100.21.*
    兄弟,请教一个问题,问题的现象同 pitt 阐述的类似,就是我设置了一个按钮,先选择树中的checkbox,然后再点击按钮提交,再在后台遍历所有的树节点,得到所有被选中的节点的值。如果全选,或者全部取消得到的值是正常的,但是如果全选后,再取消根节点下某个子节点,然后再取消根节点的选择(这时已经没有选择任何节点了),提交后得到的结果反而是第一次已经取消的那个节点的值,也就是 pitt 所说的发现节点状态正好交换了一下,不知道是为什么?不知道老兄你遇到过没有?
    #freeman 发表于2005-10-27 10:22:00  IP: 211.100.21.*
    我也做了,pitt 说的问题,存在,问题的现象不好描述,多点几次,然后再提交服务器端获得的节点与你选择的节点正好相反(选择的没有选上,没选择的选上了),老兄你遇到过这个问题吗?
    #狼 发表于2006-04-14 16:42:00  IP: 222.212.79.*
    我也遇到了同样的问题.求助!兄弟
    #kim 发表于2006-04-20 10:39:00  IP: 202.130.186.*
    现在我又发现一个问题
    这个只能实现IE
    而当浏览器为Firefox时却没效果。不知道怎么解决呢
    希望有人能帮忙回答哈!
    #Ray 发表于2006-04-21 18:28:00  IP: 59.61.126.*
    ray530@21cn.com

    急需HTC文件,楼主请给我一份吧,万分感谢``急````
    #游客 发表于2006-04-28 09:49:00  IP: 219.142.134.*
    我也急需一份
    jxm_010@163.com
    谢谢!
    #linjie 发表于2006-05-12 14:40:00  IP: 159.226.20.*
    我很需要一份HTC文件
    能给我传一份么谢谢
    jianxiu123@sohu.com
    #glxgl 发表于2006-06-08 16:59:00  IP: 222.68.22.*
    我也急需一份
    gxl@overflight.net 谢谢!
    #qingshangren 发表于2006-07-19 19:01:00  IP: 222.173.109.*
    我也需要一份可以传我下吗?如果你把这个TREEVIEW编译成DLL文件的话可以把两个都发我一下好吗!
    qingshangren@163.com
    #HFJ工作室 发表于2006-08-12 22:35:00  IP: 202.101.107.*
    呵呵,也发一份给我哈
    #晓梦迷蝴蝶 发表于2006-09-26 09:18:00  IP: 10.120.254.*
    谢谢啊,我也需要一份。
    qcsea.qw@gmail.com
    #晓梦迷蝴蝶 发表于2006-09-26 09:21:00  IP: 10.120.254.*
    谢谢啊,我也需要一份
    qcsea.qw@gmail.com
    #cuifeng 发表于2006-09-27 16:36:00  IP: 221.237.30.*
    谢谢啊,我需要一份

    cuisky@163.com
    #heartdevil 发表于2006-12-04 13:48:06  IP: 60.191.28.*
    cuike519 老大,我也需要一份啊。 能够发给我吗?我的地址是:heart-deviltwo@163.com 谢谢了。
    #heartdevil 发表于2006-12-04 13:48:16  IP: 60.191.28.*
    cuike519 老大,我也需要一份啊。 能够发给我吗?我的地址是:heart-deviltwo@163.com 谢谢了。
    #heartdevil 发表于2006-12-04 13:48:22  IP: 60.191.28.*
    cuike519 老大,我也需要一份啊。 能够发给我吗?我的地址是:heart-deviltwo@163.com 谢谢了。
    #heartdevil 发表于2006-12-04 14:06:39  IP: 60.191.28.*
    老大,给我一份啊?跪求了。 我的地址是: heart-deviltwo@163.com
    #z86362780 发表于2007-01-13 19:42:01  IP: 124.90.66.*
    我用了你这样的写法!但有问题,如果我点了一个子节点1,回传!!再点一下上次我点的子节点的父节点1,回传!!就会出现勾选子节点2的情况!!正常的话,应该是所有都不选中才对!!

    父节点1
    |
    |_子节点1
    |_子节点2

    请问你有什么方法解决不
    #z86362780 发表于2007-01-13 19:42:18  IP: 124.90.66.*
    我用了你这样的写法!但有问题,如果我点了一个子节点1,回传!!再点一下上次我点的子节点的父节点1,回传!!就会出现勾选子节点2的情况!!正常的话,应该是所有都不选中才对!!

    父节点1
    |
    |_子节点1
    |_子节点2

    请问你有什么方法解决不
    #lf_shao 发表于2007-01-15 11:12:19  IP: 60.177.108.*
    修正了下,如果节点已选择时的Bug
    function _setChildNode(el,state)
    {
    var childNodes = el.children;
    var _fireCheckedEvent=false;
    for(var i = 0 ;i<=childNodes.length-1;i++)
    {
    _fireCheckedEvent=false;
    _fireCheckedEvent=getNodeAttribute(childNodes[i],"Checked");
    if (_fireCheckedEvent==null)
    _fireCheckedEvent=false;
    if (_fireCheckedEvent!=state)
    {
    private_setAttribute(childNodes[i],"checked",state);
    _saveCheckState(childNodes[i]);
    }
    _setChildNode(childNodes[i],state);
    }
    }

    function _setParentNode(el,state)
    {
    var parentNode = el.parentElement;
    var _fireCheckedEvent=false;
    if(parentNode)
    {
    if(!_checkSiblingdNode(el))
    {
    _fireCheckedEvent=false;
    _fireCheckedEvent=parentNode.getAttribute("Checked");
    if (_fireCheckedEvent==null)
    _fireCheckedEvent=false;
    if (_fireCheckedEvent!=state)
    {
    private_setAttribute(parentNode,"Checked",state);
    _saveCheckState(parentNode);
    }
    _setParentNode(parentNode,state);
    }
    }
    }
    #heartdevil 发表于2007-01-19 15:39:22  IP:
    好东西
    #lzhdizhen 发表于2008-05-04 13:26:38  IP: 210.51.173.*
    lf_shao 提供的代码 确实解决了 节点已选择bug
    #cy2312 发表于2008-07-23 16:30:26  IP: 116.231.51.*
    noonghost@hotmail.com

    谢谢,也请给我一份
    #fmmmeck 发表于2008-08-27 14:48:57  IP: 202.99.63.*
    今天我也遇到这个问题了,多亏了这篇文章和 lf_shao 的修正 !
    #fmmmeck 发表于2008-08-27 14:49:03  IP: 202.99.63.*
    今天我也遇到这个问题了,多亏了这篇文章和 lf_shao 的修正 !
    #fmmmeck 发表于2008-08-27 14:49:33  IP: 202.99.63.*
    今天我也遇到这个问题了,多亏了这篇文章和 lf_shao 的修正 !
    #nychanglingfeng 发表于2008-10-29 19:10:38  IP: 61.163.231.*
    真是挺牛的
    #nychanglingfeng 发表于2008-10-29 19:10:46  IP: 61.163.231.*
    真是挺牛的
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © cuike519