分类: C/C++
2008-08-01 16:55:48
在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类的对象。
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 );上述代码将设置根节点不显示三态选择框。
一、 CTreeCtrl类为基类派生CMutiTreeCtrl类
class CMutiTreeCtrl : public CTreeCtrl { // Construction public: CMutiTreeCtrl(); // Attributes public: // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMutiTreeCtrl) //}}AFX_VIRTUAL // Implementation public: BOOL SetItemState( HTREEITEM hItem, UINT nState, UINT nStateMask, BOOL bSearch=TRUE); virtual ~CMutiTreeCtrl(); // Generated message map functions protected: //{{AFX_MSG(CMutiTreeCtrl) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnStateIconClick(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnKeydown(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: UINT m_uFlags; void TravelSiblingAndParent(HTREEITEM hItem, int nState); void TravelChild(HTREEITEM hItem,int nState); };二、重载CTreeCtrl的SetItemState()函数,在调用了基类的SetItemState()函数修改了节点状态以后,遍历一遍当前节点子节点、兄弟节点、父节点,按照上述逻辑修改为相应的状态,实现三态显示。调用此函数有二种情况:
BOOL CMutiTreeCtrl::SetItemState(HTREEITEM hItem, UINT nState, UINT nStateMask, BOOL bSearch) { BOOL bReturn=CTreeCtrl::SetItemState( hItem, nState, nStateMask ); UINT iState = nState >> 12; if(iState!=0) { if(bSearch) TravelChild(hItem, iState); TravelSiblingAndParent(hItem,iState); } return bReturn; }三、检测鼠标单击节点CHeckBox的事件,更改对应的节点状态并遍历树的其他节点。
void CMutiTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point) { HTREEITEM hItem =HitTest(point, &m_uFlags); if ( (m_uFlags&TVHT_ONITEMSTATEICON )) { //nState: 0->无选择钮 1->没有选择 2->部分选择 3->全部选择 UINT nState = GetItemState( hItem, TVIS_STATEIMAGEMASK ) >> 12; nState=(nState==3)?1:3; SetItemState(hItem,INDEXTOSTATEIMAGEMASK(nState),TVIS_STATEIMAGEMASK); } CTreeCtrl::OnLButtonDown(nFlags, point); } void CMutiTreeCtrl::OnStateIconClick(NMHDR* pNMHDR, LRESULT* pResult) { if(m_uFlags&TVHT_ONITEMSTATEICON) *pResult=1; else *pResult = 0; }
void CMutiTreeCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { //处理空格键 if(nChar==0x20) { HTREEITEM hItem =GetSelectedItem(); UINT nState = GetItemState( hItem, TVIS_STATEIMAGEMASK ) >> 12; if(nState!=0) { nState=(nState==3)?1:3; SetItemState( hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK ); } } else CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags); }
void CMutiTreeCtrl::TravelChild(HTREEITEM hItem, int nState) { HTREEITEM hChildItem,hBrotherItem; //查找子节点,没有就结束 hChildItem=GetChildItem(hItem); if(hChildItem!=NULL) { //设置子节点的状态与当前节点的状态一致 CTreeCtrl::SetItemState(hChildItem,INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK ); //再递归处理子节点的子节点和兄弟节点 TravelChild(hChildItem, nState); //处理子节点的兄弟节点和其子节点 hBrotherItem=GetNextSiblingItem(hChildItem); while (hBrotherItem) { //设置子节点的兄弟节点状态与当前节点的状态一致 int nState1 = GetItemState( hBrotherItem, TVIS_STATEIMAGEMASK ) >> 12; if(nState1!=0) { CTreeCtrl::SetItemState( hBrotherItem, INDEXTOSTATEIMAGEMASK(nState),TVIS_STATEIMAGEMASK ); } //再递归处理子节点的兄弟节点的子节点和兄弟节点 TravelChild(hBrotherItem, nState); hBrotherItem=GetNextSiblingItem(hBrotherItem); } } }
void CMutiTreeCtrl::TravelSiblingAndParent(HTREEITEM hItem, int nState) { HTREEITEM hNextSiblingItem,hPrevSiblingItem,hParentItem; //查找父节点,没有就结束 hParentItem=GetParentItem(hItem); if(hParentItem!=NULL) { int nState1=nState;//设初始值,防止没有兄弟节点时出错 //查找当前节点下面的兄弟节点的状态 hNextSiblingItem=GetNextSiblingItem(hItem); while(hNextSiblingItem!=NULL) { nState1 = GetItemState( hNextSiblingItem, TVIS_STATEIMAGEMASK ) >> 12; if(nState1!=nState && nState1!=0) break; else hNextSiblingItem=GetNextSiblingItem(hNextSiblingItem); } if(nState1==nState) { //查找当前节点上面的兄弟节点的状态 hPrevSiblingItem=GetPrevSiblingItem(hItem); while(hPrevSiblingItem!=NULL) { nState1 = GetItemState(hPrevSiblingItem,TVIS_STATEIMAGEMASK)>> 12; if(nState1!=nState && nState1!=0) break; else hPrevSiblingItem=GetPrevSiblingItem(hPrevSiblingItem); } } if(nState1==nState || nState1==0) { nState1 = GetItemState( hParentItem, TVIS_STATEIMAGEMASK ) >> 12; if(nState1!=0) { //如果状态一致,则父节点的状态与当前节点的状态一致 CTreeCtrl::SetItemState( hParentItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK ); } //再递归处理父节点的兄弟节点和其父节点 TravelSiblingAndParent(hParentItem,nState); } else { //状态不一致,则当前节点的父节点、父节点的父节点……状态均为第三态 hParentItem=GetParentItem(hItem); while(hParentItem!=NULL) { nState1 = GetItemState( hParentItem, TVIS_STATEIMAGEMASK ) >> 12; if(nState1!=0) { CTreeCtrl::SetItemState( hParentItem, INDEXTOSTATEIMAGEMASK(2), TVIS_STATEIMAGEMASK ); } hParentItem=GetParentItem(hParentItem); } } } }好了,一切就是这么简单,如果你还不清楚的话,那就打开工程看看吧,如你有什么问题也不要忘记来信告诉我哦!最后祝大家学习愉快,多多交流,多多进步,一切顺利! 下载本文示例代码