源碼下載
示例代碼運行效果圖如下:
在很多情況下,我們經(jīng)常需要實現(xiàn)樹的多態(tài)選擇,如上圖所示,當(dāng)全部子節(jié)點選中的情況下,當(dāng)前節(jié)點才被選中(如圖示[荊門市]節(jié)點),當(dāng)子節(jié)點部分選中時,當(dāng)前節(jié)點處于第三態(tài)(如圖示[湖北省]節(jié)點)當(dāng)全部子節(jié)點未選中時,當(dāng)前節(jié)點處于未選中的狀態(tài)(如圖示[江蘇省]節(jié)點)。本文就介紹這種三態(tài)選擇樹的具體實現(xiàn)方法。
在VC知識庫第十九期中河南科技大學(xué)叢雷朋友也介紹了一種實現(xiàn)方法,兩種方法比較,本文介紹的方法實現(xiàn)簡單,兼容原CTreeCtrl的全部操作,CheckBox也是采用控件本身的CheckBox,只是在狀態(tài)顯示時重畫而已。因此,本方法可以實現(xiàn)表示三態(tài)的情況下同時顯示節(jié)點ICON圖標(biāo),另增加了對CheckBox在某些節(jié)點是否顯示的控制,同時增加了對鍵盤空格鍵選中、取消選中的控制。具體遍歷父、子節(jié)點的方法同叢雷朋友朋友的方法類似,也是遞歸實現(xiàn)全部節(jié)點的遍歷,只是優(yōu)化了一些,效率更高。
下面介紹具體使用方法:
步驟一:生成一個對話框工程(示例工程CMutiTree)。
步驟二:添加樹控件,按照實際需要設(shè)置所需的屬性。
步驟三:做節(jié)點圖標(biāo)和三態(tài)選擇框圖標(biāo)
一般情況下節(jié)點圖標(biāo)采用16×16,三態(tài)選擇圖標(biāo)采用13×13大小比較合適。
三態(tài)選擇圖標(biāo)對應(yīng): 0->無選擇鈕 1->沒有選擇 2->部分選擇 3->全部選擇
步驟四:將兩個文件[MutiTreeCtrl.cpp ,MutiTreeCtrl.h]添加到步驟一創(chuàng)建的對話框
工程中,在CMutiTreeDlg類的頭文件中增加對[MutiTreeCtrl.h]的包含,此時工程中增加了CMutiTreeCtrl類。
#include "MutiTreeCtrl.h"
步驟五:用ClassWizard在CmutiTreeDlg中創(chuàng)建一個樹控件CTreeCtrl的對象m_TripleTree,更改該對象為上面步驟四加入的CMutiTreeCtrl類的對象。
步驟六:在CMutiTreeDlg類中定義兩個CImageList 類的對象,用于加載CMutiTreeCtrl所需要的節(jié)點圖標(biāo)列表和三態(tài)選擇框圖標(biāo)列表。
在CMutiTreeDlg類的頭文件中:
CImageList m_imgList;
CImageList m_imgState;
在對話框的初始化函數(shù)中: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);
完成以上六步操作后,編譯、運行,用鍵盤空格鍵或鼠標(biāo)單擊CheckBox改變其狀態(tài),您將看到不需要再增加任何代碼,已經(jīng)實現(xiàn)了三態(tài)選擇樹的功能。如果需要隱藏某些選擇框,如根節(jié)點的選擇框,只需要設(shè)置對應(yīng)的節(jié)點狀態(tài)為0即可: m_TripleTree.SetItemState( hRoot, INDEXTOSTATEIMAGEMASK(0),
TVIS_STATEIMAGEMASK );
上述代碼將設(shè)置根節(jié)點不顯示三態(tài)選擇框。
我具體實現(xiàn)的思想是以Windows標(biāo)準(zhǔn)的CTreeCtrl類為基類派生一個類CMutiTreeCtrl,截獲鍵盤和鼠標(biāo)點擊CheckBox的事件,在此消息響應(yīng)函數(shù)中,更改CheckBox的狀態(tài),并搜索子節(jié)點、兄弟節(jié)點和父節(jié)點,更改其狀態(tài)與上述邏輯一致。方法如下介紹:
一、 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()函數(shù),在調(diào)用了基類的SetItemState()函數(shù)修改了節(jié)點狀態(tài)以后,遍歷一遍當(dāng)前節(jié)點子節(jié)點、兄弟節(jié)點、父節(jié)點,按照上述邏輯修改為相應(yīng)的狀態(tài),實現(xiàn)三態(tài)顯示。調(diào)用此函數(shù)有二種情況:
?、冁I盤或鼠標(biāo)輸入修改節(jié)點狀態(tài),此時要遍歷全部父、兄、子節(jié)點;
②程序根據(jù)實際情況調(diào)用修改節(jié)點狀態(tài),因為修改節(jié)點狀態(tài)時是判斷了全部子節(jié)點的狀態(tài)后得出了狀態(tài),所以此時僅需要遍歷全部的兄、父節(jié)點,更改其狀態(tài)符合邏輯。故在重載的函數(shù)后面加了一個缺省為TRUE的bSearch變量,當(dāng)程序修改節(jié)點時請置此標(biāo)志為FALSE。 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;
}
三、檢測鼠標(biāo)單擊節(jié)點CHeckBox的事件,更改對應(yīng)的節(jié)點狀態(tài)并遍歷樹的其他節(jié)點。 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;
}