VC6 三态树的实现

来源:互联网 发布:数据交换标准规范 编辑:程序博客网 时间:2024/05/17 08:57

VC6 三态树的实现

 转自:http://blog.csdn.net/shellching/article/details/7050573

示例代码运行效果图如下:


在很多情况下,我们经常需要实现树的多态选择,如上图所示,当全部子节点选中的情况下,当前节点才被选中(如图示[荆门市]节点),当子节点部分选中时,当前节点处于第三态(如图示[湖北省]节点)当全部子节点未选中时,当前节点处于未选中的状态(如图示[江苏省]节点)。本文就介绍这种三态选择树的具体实现方法。

VC知识库第十九期中河南科技大学丛雷朋友也介绍了一种实现方法,两种方法比较,本文介绍的方法实现简单,兼容原CTreeCtrl的全部操作,CheckBox也是采用控件本身的CheckBox,只是在状态显示时重画而已。因此,本方法可以实现表示三态的情况下同时显示节点ICON图标,另增加了对CheckBox在某些节点是否显示的控制,同时增加了对键盘空格键选中、取消选中的控制。具体遍历父、子节点的方法同丛雷朋友朋友的方法类似,也是递归实现全部节点的遍历,只是优化了一些,效率更高。

下面介绍具体使用方法:

步骤一:生成一个对话框工程(示例工程CMutiTree)。
步骤二:添加树控件,按照实际需要设置所需的属性。
步骤三:做节点图标和三态选择框图标



一般情况下节点图标采用16×16,三态选择图标采用13×13大小比较合适。
三态选择图标对应: 0->无选择钮 1->没有选择 2->部分选择 3->全部选择

步骤四:将两个文件[MutiTreeCtrl.cpp ,MutiTreeCtrl.h]添加到步骤一创建的对话框

工程中,在CMutiTreeDlg类的头文件中增加对[MutiTreeCtrl.h]的包含,此时工程中增加了CMutiTreeCtrl类。

#include "MutiTreeCtrl.h"

步骤五:用ClassWizard在CmutiTreeDlg中创建一个树控件CTreeCtrl的对象m_TripleTree,更改该对象为上面步骤四加入的CMutiTreeCtrl类的对象。

步骤六:在CMutiTreeDlg类中定义两个CImageList 类的对象,用于加载CMutiTreeCtrl所需要的节点图标列表和三态选择框图标列表。
在CMutiTreeDlg类的头文件中:

CImageList m_imgList; CImageList m_imgState;

在对话框的初始化函数中:

m_imgState.Create(IDB_BITMAP_STATE,13, 1, RGB(255,255,255)); m_imgList.Create(IDB_BITMAP_LIST,16, 1, RGB(255,255,255)); m_TripleTree.SetImageList(&m_imgList,TVSIL_NORMAL); m_TripleTree.SetImageList(&m_imgState,TVSIL_STATE);

完成以上六步操作后,编译、运行,用键盘空格键或鼠标单击CheckBox改变其状态,您将看到不需要再增加任何代码,已经实现了三态选择树的功能。如果需要隐藏某些选择框,如根节点的选择框,只需要设置对应的节点状态为0即可:

m_TripleTree.SetItemState( hRoot, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK );

上述代码将设置根节点不显示三态选择框。
我具体实现的思想是以Windows标准的CTreeCtrl类为基类派生一个类CMutiTreeCtrl,截获键盘和鼠标点击CheckBox的事件,在此消息响应函数中,更改CheckBox的状态,并搜索子节点、兄弟节点和父节点,更改其状态与上述逻辑一致。方法如下介绍:

 一、 CTreeCtrl类为基类派生CMutiTreeCtrl类

 

[cpp] view plaincopy
  1. class CMutiTreeCtrl : public CTreeCtrl  
  2. {  
  3. // Construction  
  4. public:  
  5.     CMutiTreeCtrl();  
  6. // Attributes  
  7. public:  
  8. // Operations  
  9. public:  
  10. // Overrides  
  11.     // ClassWizard generated virtual function overrides  
  12.     //{{AFX_VIRTUAL(CMutiTreeCtrl)  
  13.     //}}AFX_VIRTUAL  
  14. // Implementation  
  15. public:  
  16.     BOOL SetItemState( HTREEITEM hItem, UINT nState, UINT nStateMask, BOOL bSearch=TRUE);  
  17.     virtual ~CMutiTreeCtrl();  
  18.     // Generated message map functions  
  19. protected:  
  20.     //{{AFX_MSG(CMutiTreeCtrl)  
  21.     afx_msg void OnLButtonDown(UINT nFlags, CPoint point);  
  22.     afx_msg void OnStateIconClick(NMHDR* pNMHDR, LRESULT* pResult);  
  23.     afx_msg void OnKeydown(NMHDR* pNMHDR, LRESULT* pResult);  
  24.     afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);  
  25.     //}}AFX_MSG  
  26.     DECLARE_MESSAGE_MAP()  
  27. private:  
  28.     UINT m_uFlags;  
  29.     void TravelSiblingAndParent(HTREEITEM hItem, int nState);  
  30.     void TravelChild(HTREEITEM hItem,int nState);  
  31. };  


二、重载CTreeCtrl的SetItemState()函数在调用了基类的SetItemState()函数修改了节点状态以后,遍历一遍当前节点子节点、兄弟节点、父节点,按照上述逻辑修改为相应的状态,实现三态显示。调用此函数有二种情况:
①键盘或鼠标输入修改节点状态,此时要遍历全部父、兄、子节点;
②程序根据实际情况调用修改节点状态,因为修改节点状态时是判断了全部子节点的状态后得出了状态,所以此时仅需要遍历全部的兄、父节点,更改其状态符合逻辑。故在重载的函数后面加了一个缺省为TRUE的bSearch变量,当程序修改节点时请置此标志为FALSE。

[cpp] view plaincopy
  1. BOOL CMutiTreeCtrl::SetItemState(HTREEITEM hItem, UINT nState,  
  2. UINT nStateMask, BOOL bSearch)  
  3. {  
  4.     BOOL bReturn=CTreeCtrl::SetItemState( hItem, nState, nStateMask );  
  5.   
  6.     UINT iState = nState >> 12;  
  7.     if(iState!=0)  
  8.     {  
  9.         if(bSearch) TravelChild(hItem, iState);  
  10.         TravelSiblingAndParent(hItem,iState);  
  11.     }  
  12.     return bReturn;  
  13. }  


 

三、检测鼠标单击节点CHeckBox的事件,更改对应的节点状态并遍历树的其他节点。

[cpp] view plaincopy
  1. void CMutiTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point)   
  2. {  
  3.     HTREEITEM hItem =HitTest(point, &m_uFlags);  
  4.     if ( (m_uFlags&TVHT_ONITEMSTATEICON ))  
  5.     {  
  6.         //nState: 0->无选择钮 1->没有选择 2->部分选择 3->全部选择  
  7.         UINT nState = GetItemState( hItem, TVIS_STATEIMAGEMASK ) >> 12;  
  8.         nState=(nState==3)?1:3;  
  9.         SetItemState(hItem,INDEXTOSTATEIMAGEMASK(nState),TVIS_STATEIMAGEMASK);  
  10.     }  
  11.       
  12.     CTreeCtrl::OnLButtonDown(nFlags, point);  
  13. }  
  14. void CMutiTreeCtrl::OnStateIconClick(NMHDR* pNMHDR, LRESULT* pResult)   
  15. {  
  16.     if(m_uFlags&TVHT_ONITEMSTATEICON) *pResult=1;  
  17.     else *pResult = 0;  
  18. }  


四、检测键盘按空格键的事件,更改对应的节点状态并遍历树的其他节点。

[cpp] view plaincopy
  1. void CMutiTreeCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)   
  2. {  
  3.     //处理空格键  
  4.     if(nChar==0x20)  
  5.     {  
  6.         HTREEITEM hItem =GetSelectedItem();  
  7.         UINT nState = GetItemState( hItem, TVIS_STATEIMAGEMASK ) >> 12;  
  8.         if(nState!=0)  
  9.         {  
  10.             nState=(nState==3)?1:3;  
  11.             SetItemState( hItem, INDEXTOSTATEIMAGEMASK(nState),  
  12.  TVIS_STATEIMAGEMASK );  
  13.         }  
  14.     }  
  15.     else CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);  
  16. }  


 五、树的遍历用递归的方法搜索当前节点的父、兄、子节点

①递归搜索子节点

[cpp] view plaincopy
  1. void CMutiTreeCtrl::TravelChild(HTREEITEM hItem, int nState)  
  2. {  
  3.     HTREEITEM hChildItem,hBrotherItem;  
  4.       
  5.     //查找子节点,没有就结束  
  6.     hChildItem=GetChildItem(hItem);  
  7.     if(hChildItem!=NULL)  
  8.     {  
  9.         //设置子节点的状态与当前节点的状态一致  
  10.         CTreeCtrl::SetItemState(hChildItem,INDEXTOSTATEIMAGEMASK(nState),  
  11.                                 TVIS_STATEIMAGEMASK );  
  12.         //再递归处理子节点的子节点和兄弟节点  
  13.         TravelChild(hChildItem, nState);  
  14.           
  15.         //处理子节点的兄弟节点和其子节点  
  16.         hBrotherItem=GetNextSiblingItem(hChildItem);  
  17.         while (hBrotherItem)  
  18.         {  
  19.             //设置子节点的兄弟节点状态与当前节点的状态一致  
  20.             int nState1 = GetItemState( hBrotherItem, TVIS_STATEIMAGEMASK ) >> 12;  
  21.             if(nState1!=0)  
  22.             {  
  23.                 CTreeCtrl::SetItemState( hBrotherItem,  
  24.                     INDEXTOSTATEIMAGEMASK(nState),TVIS_STATEIMAGEMASK );  
  25.             }  
  26.             //再递归处理子节点的兄弟节点的子节点和兄弟节点  
  27.             TravelChild(hBrotherItem, nState);  
  28.             hBrotherItem=GetNextSiblingItem(hBrotherItem);  
  29.         }  
  30.     }  
  31. }  


②递归搜索兄、父节点

[cpp] view plaincopy
  1. void CMutiTreeCtrl::TravelSiblingAndParent(HTREEITEM hItem, int nState)  
  2. {  
  3.     HTREEITEM hNextSiblingItem,hPrevSiblingItem,hParentItem;  
  4.       
  5.     //查找父节点,没有就结束  
  6.     hParentItem=GetParentItem(hItem);  
  7.     if(hParentItem!=NULL)  
  8.     {  
  9.         int nState1=nState;//设初始值,防止没有兄弟节点时出错  
  10.           
  11.         //查找当前节点下面的兄弟节点的状态  
  12.         hNextSiblingItem=GetNextSiblingItem(hItem);  
  13.         while(hNextSiblingItem!=NULL)  
  14.         {  
  15.             nState1 = GetItemState( hNextSiblingItem, TVIS_STATEIMAGEMASK ) >> 12;  
  16.             if(nState1!=nState && nState1!=0) break;  
  17.             else hNextSiblingItem=GetNextSiblingItem(hNextSiblingItem);  
  18.         }  
  19.           
  20.         if(nState1==nState)  
  21.         {  
  22.             //查找当前节点上面的兄弟节点的状态  
  23.             hPrevSiblingItem=GetPrevSiblingItem(hItem);  
  24.             while(hPrevSiblingItem!=NULL)  
  25.             {  
  26.                 nState1 = GetItemState(hPrevSiblingItem,TVIS_STATEIMAGEMASK)>> 12;  
  27.                 if(nState1!=nState && nState1!=0) break;  
  28.                 else hPrevSiblingItem=GetPrevSiblingItem(hPrevSiblingItem);  
  29.             }  
  30.         }  
  31.           
  32.         if(nState1==nState || nState1==0)  
  33.         {  
  34.             nState1 = GetItemState( hParentItem, TVIS_STATEIMAGEMASK ) >> 12;  
  35.             if(nState1!=0)  
  36.             {  
  37.                 //如果状态一致,则父节点的状态与当前节点的状态一致  
  38.                 CTreeCtrl::SetItemState( hParentItem,   
  39.                     INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK );  
  40.             }  
  41.             //再递归处理父节点的兄弟节点和其父节点  
  42.             TravelSiblingAndParent(hParentItem,nState);  
  43.         }  
  44.         else  
  45.         {  
  46.             //状态不一致,则当前节点的父节点、父节点的父节点……状态均为第三态  
  47.             hParentItem=GetParentItem(hItem);  
  48.             while(hParentItem!=NULL)  
  49.             {  
  50.                 nState1 = GetItemState( hParentItem, TVIS_STATEIMAGEMASK ) >> 12;  
  51.                 if(nState1!=0)  
  52.                 {  
  53.                     CTreeCtrl::SetItemState( hParentItem,   
  54.                         INDEXTOSTATEIMAGEMASK(2), TVIS_STATEIMAGEMASK );  
  55.                 }  
  56.                 hParentItem=GetParentItem(hParentItem);  
  57.             }  
  58.         }  
  59.     }     
  60. }  

 

好了,一切就是这么简单,如果你还不清楚的话,那就打开工程看看吧,如你有什么问题也不要忘记来信告诉我哦!最后祝大家学习愉快,多多交流,多多进步,一切顺利!