版权声明:可以任意转载,转载时请务必以超链接形式标明如下文章原始出处和作者信息及本声明
作者:xixi
出处:http://blog.csdn.net/slowgrace/archive/2008/12/05/3456729.aspx
多挂节点引起的多树联动问题给我带来了不少工作量。我想破了脑袋最后的解决办法是,给annatree封装一个annatrees集合变量,每次有事件发生的时候,就遍历annatrees里的annatree,挨个做相应的修改。我还费劲吧啦的写了个用动态数组做byref参数的函数,用来获得需要处理的tree和node。按照这个思路,想清楚、coding完、逐分支测试完,而且也编完、测试完大多数相关的需要处理多树联动的事件过程了。我忽然觉得这个方案不符合OOP的理念。
OOP嘛,都是自己负责自己的事。我现在怎么能直接就设置别人的tree的node,这不是干涉别国内政么?我心乱如麻地想了半天,不明白当时怎么会允许自己这么做呢?当时我好像是基于这篇文章的思路,觉得我只要改变事主的一些属性,不需要用事件过程;而因为这些改变属性都很简单,一个语句了事,我懒得再另外发明一个方法、再调用;于是就形成现在这个局面。
这么呆了一会儿,发现我面临两个选择,如果一定要OOP的话。第一,是编造方法,把那些改人家节点的行为都搬到人家自己的地盘里,然后在涉及多树联动的事件过程里调用那些方法;第二,是触发事件。然后各自在事件过程里处理自己该做的变动。
两个选择的代码量差不多,我想选其中一个比较elegant的,就是看起来比较赏心悦目,可读性好、维护性好的。这主要是比较调用的时候谁更elegant,因为处理过程都基于相同的算法,没啥大区别。
如果是方法,调用的时候,我就for each tree, tree.某方法(方法名随不同事件而不同);如果是事件过程,调用的时候,我就for each tree, tree.m_mate.re(不同事件的处理包含在这个过程里,调用的时候用相同的参数)。
这么写着,我好像就倾向于后面这个方案。虽然后面这个方案多绕一个弯,效率上必然稍稍多花点时间,不过好在我的树不多,这个效率的损失是可忽略的。唉。我觉得自己就像个OOP狂。 我决定一会儿把tree改成私有属性,不再允许别人得到它了,这样以后我即使想不OOP,都编译通不过了。
对了,这第二种基于事件的方案说来还挺绕的,忍不住想显摆一哈。看图说话吧。
首先,看上图,annatree类的构造,它实际上是对treeview的封装,为的是能把相应的事件过程也打包在一起,我称它为绕着圈子继承类。其中的主要成员变量有m_tree, m_mate, m_trees。m_tree就是treeview类;m_mate的类型是我自己造的一个类,用来raise我自己造的event,m_trees则是annatree的集合,维护了当前所有存活的annatree,通过遍历这个集合中的每个item,就可以处理多树联动问题。
其次,我们来具体说明一下,如何用事件机制解决多树联动问题。比如说,我click了annatree1树上的一个节点,多树联动就要求其他n-1棵annatree的树也做出相应变动。
其实也简单,看上图(上面的图被缩得比较小,点进去看大图哦):首先用户点击树1的节点,触发annatree1的m_tree_nodeclick()事件过程;其次在该事件过程里,我调用其他各annatree的m_mate的触发事件的方法,触发了各annatree的m_mate的NC事件;然后各annatree的m_mate的nc事件过程被调用。
或者简单点说,像上图这样,AnnaTree1的m_tree的nodeclick事件,触发了annatreeN的m_mate的nc()事件过程。
于是,就像上图这样,我们完成了神奇的过程:用户点击treeview1,和它8杆子打不着的treeview2却自动作出了响应。这里面的诀窍就是在treeview1的nodeclick事件过程中,轮询所有其他treeview,触发它们的事件。
但其实我们还可以做得更狠一点,压根就不用轮询其他treeview,就能让所有treeview知道其中一个treeview发生了某事件,其秘诀就是用m_trees的事件。还是来看图说话(记得点击看大图哦)。
假设我们还是要解决“在树1上点,其他树都抖一抖”的问题,那么我们现在要介绍的这个很绝的解决方案步骤如下:
(1)用户点击树1,触发树1的nodeclick事件;
(2)树1的m_tree_nodeclick()事件过程被调用;
(3)在该过程中,调用m_trees成员变量的方法
(4)在该方法中,包含raiseEvent Nc语句。
(5)该语句使系统的事件机制触发了m_trees所指向的对象(gobjATress)的NC事件。
(6)由于所有其他ANNATREE对象的m_trees成员变量都指向该gobjATress对象,所以,实际上所有ANNATREE对象的m_trees变量都被触发了NC事件
(7)从而,所有annatree对象的m_trees变量的m_trees_nc过程都被掉用了。
假设我们把上面第(2)步到第(6)步都框起来,藏到粉红盒子里,那么我们看到的效果就是,用户点击树1,其他所有树都抖了一哈(发生了响应)。呵呵。这个过程涉及的coding主要包括:
(0)在AnnaTrees模块中,用event语句定义事件
(1)在annatrees模块中,在某public方法中,写raiseevent语句,触发上述事件。
(2)在annatree模块中,在m_tree的事件过程里,调用m_trees的上述方法;
(3)在annatree模块中,在m_trees的相应事件过程中,写联动响应代码。
显然,上面这个过程中,没有涉及显式的m_trees的轮询,是不是有点绝呀?
我们罗嗦了这么多,其实整个过程实质上就是:
(1)树1在发生A事件之后,进行相应的处理;
(2)处理完毕之后给其他树打招呼:“嗨,哥们儿,我这儿哈发生了这么个事,你们该咋咋地吧”
(3)其他树接到通知后,就自动作出调整,适应这个新情况。
其中第二步的实现机制呢,有两种方法:第一种方法是用m_mate,需要轮询;第二种方法,用m_trees,不需要轮询。之所以不需要,是因为这所有树都有一个指向共同的公共对象的成员对象指针,所以只要给其中的一个对象指针传递消息,其他对象就自动会被通知到了,相当于系统暗地里义务地给咱做了轮询。
最后,附赠第二种方案的第二种方法的代码。在AnnaTrees模块中:
- Public Event Nice(strName As String)
- Public Sub SendMsg(strName As String)
- RaiseEvent Nice(strName)
- End Sub
在AnnaTree模块中:
- Private WithEvents m_trees As AnnaTrees
- Private Sub m_tree_NodeClick(ByVal Node As MSComctlLib.Node)
- 'for test
- m_trees.SendMsg (m_strTreeWnd & "-" & m_strTreeTable)
- End Sub
- Private Sub m_trees_Nice(strName As String)
- DisplayMessage "来自" & m_strTreeWnd & Space(1) & m_strTreeTable & "的报道:" & Chr(13) & strName & "给俺发了消息"
- End Sub
用户点击某树后,像下面这样的对话框先后弹出7、8个。
另外,跟踪代码的执行过程,发现系统在执行sendmsg中的raiseevent语句之后,会立刻执行相关的事件过程,这些过程执行完毕之后,才会返回来执行raiseevent之后的语句。调用堆栈如下:
更多树类文章