首先创建CXPTreeCtrl继承与CTreeCtrl,下面是头文件
class CXPTreeCtrl : public CTreeCtrl{// Constructionpublic: CXPTreeCtrl();// Attributespublic: HBITMAP m_hBmpScrollBar ;// Operationspublic: void SetCheckBoxBitMap(UINT ID)//在主对话框中使用,用于加载checkbox位图 { m_imgState.Create(ID,13, 1, RGB(255,255,255)); SetImageList(&m_imgState,TVSIL_STATE); }// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CXPTreeCtrl) //}}AFX_VIRTUAL// Implementationpublic: BOOL SetItemState(HTREEITEM hItem, UINT nState, UINT nStateMask, BOOL bSearch=TRUE); virtual ~CXPTreeCtrl(); // Generated message map functionsprotected: //{{AFX_MSG(CXPTreeCtrl) afx_msg void OnClick(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); 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); CImageList m_imgState;};
然后是实现文件
// XPTreeCtrl.cpp : implementation file//#include "stdafx.h"#include "XPTreeCtrl.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif/////////////////////////////////////////////////////////////////////////////// CXPTreeCtrlCXPTreeCtrl::CXPTreeCtrl(){ m_uFlags=0;}CXPTreeCtrl::~CXPTreeCtrl(){}BEGIN_MESSAGE_MAP(CXPTreeCtrl, CTreeCtrl) //{{AFX_MSG_MAP(CXPTreeCtrl) ON_NOTIFY_REFLECT(NM_CLICK, OnClick) ON_WM_LBUTTONDOWN() ON_WM_KEYDOWN() //}}AFX_MSG_MAPEND_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////// CXPTreeCtrl message handlersvoid CXPTreeCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult) { if(m_uFlags&TVHT_ONITEMSTATEICON) *pResult=1; else *pResult = 0;}void CXPTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point) { HTREEITEM hItem =HitTest(point, &m_uFlags); if (hItem != NULL) { SetFocus(); SelectItem(hItem); } 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 CXPTreeCtrl::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);}BOOL CXPTreeCtrl::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;}void CXPTreeCtrl::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 CXPTreeCtrl::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); } } } }
以上就实现了三态树的所有功能,接下来是使用方法
假设主对话框是CMyDlg,此时已经通过DDX添加了树形控件CTreeCtrl m_treeCtrl
把CTreeCtrl m_treeCtrl 改为 CXPTreeCtrl m_treeCtrl
重载BOOL OnInitDialog();
在其中添加以下代码,主要是加载checkbox的位图
//加载树形控件的资源,假设加载的位图ID是IDB_MultiTreeCheckBoxm_treeCtrl.SetCheckBoxBitMap(IDB_MultiTreeCheckBox);
如果要获取当前checkbox的状态
//其中 nState: 0->无选择钮 1->没有选择 2->部分选择 3->全部选择UINT nState = m_treeCtrl.GetItemState( cItem, TVIS_STATEIMAGEMASK ) >> 12;
设置当前checkbox的状态
//SetItemState()是被重载过的函数,因此这样设置后,父子节点会保持一致//同样 0->无选择钮 1->没有选择 2->部分选择 3->全部选择,数字填在INDEXTOSTATEIMAGEMASK()中m_treeCtrl.SetItemState(item, INDEXTOSTATEIMAGEMASK(3), TVIS_STATEIMAGEMASK );
整体的三态树形控件就是这样创建和使用的。
联系客服