三種狀態的CheckBox,CTreeView

來源:互聯網
上載者:User

 

 

http://www.vckbase.com/document/viewdoc/?id=665

三態選擇樹實現終結者
作者:南京銀山網路有限公司 王佳豪

下載本文樣本原始碼

範例程式碼運行如下:

在很多情況下,我們經常需要實現樹的多態選擇,如所示,當全部子節點選中的情況下,當前節點才被選中(示[荊門市]節點),當子節點部分選中時,當前節點處於第三態(示[湖北省]節點)當全部子節點未選中時,當前節點處於未選中的狀態(示[江蘇省]節點)。本文就介紹這種三態選擇樹的具體實現方法。

在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類

class CMutiTreeCtrl : public CTreeCtrl{// Constructionpublic:CMutiTreeCtrl();// Attributespublic:// Operationspublic:// Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CMutiTreeCtrl)//}}AFX_VIRTUAL// Implementationpublic:BOOL SetItemState( HTREEITEM hItem, UINT nState, UINT nStateMask, BOOL bSearch=TRUE);virtual ~CMutiTreeCtrl();// Generated message map functionsprotected://{{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_MSGDECLARE_MESSAGE_MAP()private:UINT m_uFlags;void TravelSiblingAndParent(HTREEITEM hItem, int nState);void TravelChild(HTREEITEM hItem,int nState);};

二、重載CTreeCtrl的SetItemState()函數,在調用了基類的SetItemState()函數修改了節點狀態以後,遍曆一遍當前節點子節點、兄弟節點、父節點,按照上述邏輯修改為相應的狀態,實現三態顯示。調用此函數有二種情況:
①鍵盤或滑鼠輸入修改節點狀態,此時要遍曆全部父、兄、子節點;
②程式根據實際情況調用修改節點狀態,因為修改節點狀態時是判斷了全部子節點的狀態後得出了狀態,所以此時僅需要遍曆全部的兄、父節點,更改其狀態符合邏輯。故在重載的函數後面加了一個預設為TRUE的bSearch變數,當程式修改節點時請置此標誌為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;}

三、檢測按一下滑鼠節點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);}}}}

好了,一切就是這麼簡單,如果你還不清楚的話,那就開啟工程看看吧,如你有什麼問題也不要忘記來信告訴我哦!最後祝大家學習愉快,多多交流,多多進步,一切順利!

http://www.vckbase.com/document/viewdoc/?id=665

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.