1. 程式人生 > >MFC樹形控制元件(CTreeCtrl)用法(上)

MFC樹形控制元件(CTreeCtrl)用法(上)

樹形控制元件可以用於樹形的結構,其中有一個根接點(Root)然後下面有許多子結點,而每個子結點上有允許有一個或多個或沒有子結點。MFC中使用CTreeCtrl類來封裝樹形控制元件的各種操作。通過呼叫
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );建立一個視窗,dwStyle中可以使用以下一些樹形控制元件的專用風格:
TVS_HASLINES 在父/子結點之間繪製連線
TVS_LINESATROOT 在根/子結點之間繪製連線
TVS_HASBUTTONS 在每一個結點前新增一個按鈕,用於表示當前結點是否已被展開
TVS_EDITLABELS 結點的顯示字元可以被編輯
TVS_SHOWSELALWAYS 在失去焦點時也顯示當前選中的結點
TVS_DISABLEDRAGDROP 不允許Drag/Drop
TVS_NOTOOLTIPS 不使用ToolTip顯示結點的顯示字元
在樹形控制元件中每一個結點都有一個控制代碼(HTREEITEM),同時新增結點時必須提供的引數是該結點的父結點控制代碼,(其中根Root結點只有一個,既不可以新增也不可以刪除)利用
HTREEITEM InsertItem( LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST );可以新增一個結點,pszItem為顯示的字元,hParent代表父結點的控制代碼,當前新增的結點會排在hInsertAfter表示的結點的後面, 返回值為當前建立的結點的控制代碼。下面的程式碼會建立一個如下形式的樹形結構:
+--- Parent1
    +--- Child1_1
    +--- Child1_2
    +--- Child1_3
+--- Parent2
+--- Parent3
/*假設m_tree為一個CTreeCtrl物件,而且該視窗已經建立*/
HTREEITEM hItem,hSubItem;
hItem = m_tree.InsertItem("Parent1",TVI_ROOT);在根結點上新增Parent1
hSubItem = m_tree.InsertItem("Child1_1",hItem);//在Parent1上新增一個子結點
hSubItem = m_tree.InsertItem("Child1_2",hItem,hSubItem);//在Parent1上新增一個子結點,排在Child1_1後面
hSubItem = m_tree.InsertItem("Child1_3",hItem,hSubItem);
hItem = m_tree.InsertItem("Parent2",TVI_ROOT,hItem);   
hItem = m_tree.InsertItem("Parent3",TVI_ROOT,hItem);  
如 果你希望在每個結點前新增一個小圖示,就必需先呼叫CImageList* SetImageList( CImageList * pImageList, int nImageListType );指明當前所使用的ImageList,nImageListType為TVSIL_NORMAL。在呼叫完成後控制元件中使用圖片以設定的 ImageList中圖片為準。然後呼叫
HTREEITEM InsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST);新增結點,nImage為結點沒被選中時所使用圖片序號,nSelectedImage為結點被選中時所使用圖片序號。下面的程式碼演 示了ImageList的設定。
/*m_list 為CImageList物件
IDB_TREE 為16*(16*4)的點陣圖,每個圖片為16*16共4個圖示*/
m_list.Create(IDB_TREE,16,4,RGB(0,0,0));
m_tree.SetImageList(&m_list,TVSIL_NORMAL);
m_tree.InsertItem("Parent1",0,1);//新增,選中時顯示圖示1,未選中時顯示圖示0
$nextPage$
此外CTreeCtrl還提供了一些函式用於得到/修改控制元件的狀態。
HTREEITEM GetSelectedItem( );將返回當前選中的結點的控制代碼。BOOL SelectItem( HTREEITEM hItem );將選中指明結點。
BOOL GetItemImage( HTREEITEM hItem, int& nImage, int& nSelectedImage ) / BOOL SetItemImage( HTREEITEM hItem, int nImage, int nSelectedImage )用於得到/修改某結點所使用圖示索引。
CString GetItemText( HTREEITEM hItem ) /BOOL SetItemText( HTREEITEM hItem, LPCTSTR lpszItem );用於得到/修改某一結點的顯示字元。
BOOL DeleteItem( HTREEITEM hItem );用於刪除某一結點,BOOL DeleteAllItems( );將刪除所有結點。
此外如果想遍歷樹可以使用下面的函式:
HTREEITEM GetRootItem( );得到根結點。
HTREEITEM GetChildItem( HTREEITEM hItem );得到子結點。
HTREEITEM GetPrevSiblingItem/GetNextSiblingItem( HTREEITEM hItem );得到指明結點的上/下一個兄弟結點。
HTREEITEM GetParentItem( HTREEITEM hItem );得到父結點。
樹 形控制元件的訊息對映使用ON_NOTIFY巨集,形式如同:ON_NOTIFY( wNotifyCode, id, memberFxn ),wNotifyCode為通知程式碼,id為產生該訊息的視窗ID,memberFxn為處理函式,函式的原型如同void OnXXXTree(NMHDR* pNMHDR, LRESULT* pResult),其中pNMHDR為一資料結構,在具體使用時需要轉換成其他型別的結構。對於樹形控制元件可能取值和對應的資料結構為:
TVN_SELCHANGED 在所選中的結點發生改變後傳送,所用結構:NMTREEVIEW
TVN_ITEMEXPANDED 在某結點被展開後傳送,所用結構:NMTREEVIEW
TVN_BEGINLABELEDIT 在開始編輯結點字元時傳送,所用結構:NMTVDISPINFO
TVN_ENDLABELEDIT 在結束編輯結點字元時傳送,所用結構:NMTVDISPINFO
TVN_GETDISPINFO 在需要得到某結點資訊時傳送,(如得到結點的顯示字元)所用結構:NMTVDISPINFO
關於ON_NOTIFY有很多內容,將在以後的內容中進行詳細講解。
關 於動態提供結點所顯示的字元:首先你在新增結點時需要指明lpszItem引數為:LPSTR_TEXTCALLBACK。在控制元件顯示該結點時會通過傳送 TVN_GETDISPINFO來取得所需要的字元,在處理該訊息時先將引數pNMHDR轉換為LPNMTVDISPINFO,然後填充其中 item.pszText。但是我們通過什麼來知道該結點所對應的資訊呢,我的做法是在新增結點後設置其lParam引數,然後在提供資訊時利用該引數來 查詢所對應的資訊。下面的程式碼說明了這種方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 char szOut[8][3]={"No.1","No.2","No.3"}; //新增結點 HTREEITEM hItem = m_tree.InsertItem(LPSTR_TEXTCALLBACK,...) m_tree.SetItemData(hItem, 0 ); hItem = m_tree.InsertItem(LPSTR_TEXTCALLBACK,...) m_tree.SetItemData(hItem, 1 ); //處理訊息 void CParentWnd::OnGetDispInfoTree(NMHDR* pNMHDR, 
LRESULT* pResult) { TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR; pTVDI->item.pszText=szOut[pTVDI->item.lParam]; //通過lParam得到需要顯示的字元在陣列中的位置 *pResult = 0; }

關 於編輯結點的顯示字元:首先需要設定樹形控制元件的TVS_EDITLABELS風格,在開始編輯時該控制元件將會發送TVN_BEGINLABELEDIT,你 可以通過在處理函式中返回TRUE來取消接下來的編輯,在編輯完成後會發送TVN_ENDLABELEDIT,在處理該訊息時需要將引數pNMHDR轉換 為LPNMTVDISPINFO,然後通過其中的item.pszText得到編輯後的字元,並重置顯示字元。如果編輯在中途中取消該變數為NULL。下 面的程式碼說明如何處理這些訊息:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 //處理訊息 TVN_BEGINLABELEDIT void CParentWnd::OnBeginEditTree(NMHDR* pNMHDR, LRESULT* pResult) { TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR; if(pTVDI->item.lParam==0);//判斷是否取消該操作 *pResult = 1; else *pResult = 0; } //處理訊息 TVN_BEGINLABELEDIT void CParentWnd::OnBeginEditTree(NMHDR* pNMHDR, LRESULT* pResult) { TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR; if(pTVDI->item.pszText==NULL);//判斷是否已經取消取消編輯 m_tree.SetItemText(pTVDI->item.hItem,pTVDI->pszText);//重置顯示字元 *pResult = 0; }
上面講述的方法所進行的訊息對映必須在父視窗中進行(同樣WM_NOTIFY的所有訊息都需要在父視窗中處理)。