头文件#if !defined(AFX_MUTITREECTRL_H__1298A0CF_BFBF_414E_A1E1_BA18D9B39FBB__INCLUDED_) #define AFX_MUTITREECTRL_H__1298A0CF_BFBF_414E_A1E1_BA18D9B39FBB__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // MutiTreeCtrl.h : header file // / // CMutiTreeCtrl window class CMutiTreeCtrl : public CTreeCtrl { // Construction public: CMutiTreeCtrl(); // Attributes public: // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMutiTreeCtrl) //}}AFX_VIRTUAL // Implementation public: void SetLeafParentState(HTREEITEM TreeItem); void SetParentState(HTREEITEM TreeItem); BOOL SetItemStateEx( 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); }; / //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_MUTITREECTRL_H__1298A0CF_BFBF_414E_A1E1_BA18D9B39FBB__INCLUDED_) 源文件 // MutiTreeCtrl.cpp : implementation file // #include "stdafx.h" #include "MutiTreeCtrl.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif / // CMutiTreeCtrl CMutiTreeCtrl::CMutiTreeCtrl() { m_uFlags=0; } CMutiTreeCtrl::~CMutiTreeCtrl() { } BEGIN_MESSAGE_MAP(CMutiTreeCtrl, CTreeCtrl) //{{AFX_MSG_MAP(CMutiTreeCtrl) ON_WM_LBUTTONDOWN() ON_NOTIFY_REFLECT(NM_CLICK, OnStateIconClick) ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown) ON_WM_KEYDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP() / // CMutiTreeCtrl message handlers void CMutiTreeCtrl::OnStateIconClick(NMHDR* pNMHDR, LRESULT* pResult) { if(m_uFlags&TVHT_ONITEMSTATEICON) *pResult=1; else *pResult = 0; } 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; SetItemStateEx( hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK ); // 使节点呈选中状态 SelectItem(hItem); } CTreeCtrl::OnLButtonDown(nFlags, point); } 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; SetItemStateEx( hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK ); } } else CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags); } void CMutiTreeCtrl::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult) { TV_KEYDOWN* pTVKeyDown = (TV_KEYDOWN*)pNMHDR; *pResult = 0; } BOOL CMutiTreeCtrl::SetItemStateEx(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 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)// 此函数中所有的nState1 != 0其实都用不着,因为状态值不可能为0,只可能为1.2.3.. { 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); } } } } /*********************************************************** Function: SetParentState Description: 遍历TreeItem的兄弟节点以决定其父节点的状态, 并递归至根节点,适用于兄弟节点比较少的情况 ***********************************************************/ void CMutiTreeCtrl::SetParentState(HTREEITEM TreeItem) { HTREEITEM hItem = TreeItem; int nAll = 0, nAllSel = 0, nPartSel = 0; // nAllSel 全选的, nPartSel 部分选的 /* 查找位于前面的兄弟节点 */ while (hItem != NULL) { UINT nState = GetItemState( hItem, TVIS_STATEIMAGEMASK ) >> 12; if (nState == 2) { nPartSel++; } else if (nState == 3) { nAllSel++; } nAll++; hItem = GetPrevSiblingItem(hItem); } /* 查找位于后面的兄弟节点 */ hItem = GetNextSiblingItem(TreeItem); while (hItem != NULL) { UINT nState = GetItemState( hItem, TVIS_STATEIMAGEMASK ) >> 12; if (nState == 2) { nPartSel++; } else if (nState == 3) { nAllSel++; } nAll++; hItem = GetNextSiblingItem(hItem); } HTREEITEM hParent = GetParentItem(TreeItem); if ((hParent != NULL) && (nAll != 0)) { if (nAllSel == nAll) { SetItemState(hParent, INDEXTOSTATEIMAGEMASK(3), TVIS_STATEIMAGEMASK); } else if (nPartSel > 0 || nAllSel > 0) { SetItemState(hParent, INDEXTOSTATEIMAGEMASK(2), TVIS_STATEIMAGEMASK); } SetParentState(hParent); // 递归设置父节点状态 } } /*********************************************************** Function: SetLeafParentState Description: 遍历TreeItem的叶子节点以决定TreeItem的节点状态 ************************************************************/ void CMutiTreeCtrl::SetLeafParentState(HTREEITEM TreeItem) { HTREEITEM hItem = GetChildItem(TreeItem); int nAll = 0, nSel = 0; while (hItem != NULL) { UINT nState = GetItemState( hItem, TVIS_STATEIMAGEMASK ) >> 12; if (nState == 3) { nSel++; } nAll++; hItem = GetNextItem(hItem, TVGN_NEXT); } if (nSel == nAll) { SetItemState(TreeItem, INDEXTOSTATEIMAGEMASK(3), TVIS_STATEIMAGEMASK); } else if (nSel > 0 && nSel < nAll) { SetItemState(TreeItem, INDEXTOSTATEIMAGEMASK(2), TVIS_STATEIMAGEMASK); } }