樹型視圖是一種特別的視窗,我們可以使用它一目了然地表示某種層次關係。譬如象在資源管理器中左邊視窗中的就是樹型視圖。您可以調用CreateWindowEx來創建樹型視圖,傳遞一個類名“"SysTreeView32"”,或者您也可以把它放到一個對話框中去。不要忘了在您的代碼中加入InitCommonControls函式。
樹型視圖的風格,樹型視圖控制項的數據結構,樹型視圖控制項CTreeCtrl類的主要成員函式,
樹型視圖的風格
幾種經常使用的風格。
TVS_HASBUTTONS == 在父項目中顯示(+)或(-)。用戶可以通過點擊該符號來展開或收起該父項目下的子項目。如果想在根目錄下也有這個符號必須指定TVS_LINESATROOT風格。
TVS_HASLINES == 在層次中用線條來連線各個項目名稱。
TVS_LINESATROOT == 在根目錄下的項目也用線連線。如果沒有指定TVS_HASLINES風格,該風格也就會被忽略。 像其它的通用控制項一樣,樹型視圖用訊息來完成通信。父視窗傳送一系列的訊息給樹型視圖,而樹型視圖傳送"notification"訊息給它的父視窗。在這方面,樹型視圖和其它的通用控制沒什麼兩樣。
當有事件發生時,樹型視圖傳送一個WM_NOTIFY訊息個父視窗,並在訊息中附帶傳遞一些附加信息。
WM_NOTIFY
lParam == 指向NMHDR結構體的指針。有一些控制項可能傳遞一個指向更大一點的結構體的指針。但該結構體必須保證它的第一個成員變數是一個NMHDR型的變數。這樣,您在處理lParam變數時,至少可以得到一個NMHDR型的變數。 下面我們來看NMHDR:
NMHDR struct DWORD
hwndFrom DWORD ?
idFrom DWORD ?
code DWORD ?
idFrom是傳送WM_NOTIFY訊息的控制項的ID。
code是控制項傳送給父視窗的數據。
樹型視圖傳送給父視窗的通知訊息以TVN_打頭。 樹型視圖接收到的訊息以TVM_打頭,譬如:TVM_CREATEDRAGIMAGE。 樹型視圖傳送TVN_XXX訊息時在code變數中放入NMHDR型變數。父視窗傳送TVM_訊息來控制樹型視圖。
在樹型視圖中加入項目在創建完樹型視圖後可以通過傳送TVM_INSERTITEM訊息往其中加入項目了。
TVM_INSERTITEM
wParam = 0;
您應當知道一些關於樹型視圖中的項目之間關係的一些術語。 一個項目可能是一個父親、兒子或兩者都是。父項目下含有子項目,而該父項目又有可能是其它項目的子項目。一個沒有父項目的項目叫根項目。在樹型視圖中可能有多個根項目。現在我們來看看TV_INSERTSTRUCT結構體:
TV_INSERTSTRUCT STRUCT DWORD
hParent DWORD ?
hInsertAfter DWORD ?
ITEMTYPE <>
TV_INSERTSTRUCT ENDShParent = 父項目的句柄。如果該值為TVI_ROOT value或NULL,該項目插在樹型視圖的根部。
hInsertAfter = 應該插入在起後面的項目的句柄或下面的值:
TVI_FIRST ==> 插在列表的頭部。 TVI_LAST ==> 插在列表的尾部。 TVI_SORT ==> 按字母順序插入。 ITEMTYPE UNION
itemex TVITEMEX <>
item TVITEM <>
ITEMTYPE ENDS 我們僅使用TVITEM。
TV_ITEM STRUCT DWORD
imask DWORD ?
hItem DWORD ?
state DWORD ?
stateMask DWORD ?
pszText DWORD ?
cchTextMax DWORD ?
iImage DWORD ?
iSelectedImage DWORD ?
cChildren DWORD ?
lParam DWORD ?
TV_ITEM ENDS 該結構體根據訊息類型,用來傳送或接收關於一個樹型視圖的項目的有關信息。譬如:對於訊息TVM_INSERTITEM,它用來指定插入樹型視圖控制項的項目的屬性。而對於訊息TVM_GETITEM,該結構體用來填充關於選定項目的信息。
hItem 是樹型視圖項目的句柄。每一個項目都有它自己的句柄,就像視窗一樣。如果您想要操作一個項目,就必須選擇它的句柄。
pszText 是一個字元串指針。它是項目的標籤名。
cchTextMax僅在查詢項目的名稱時使用。由於在pszText中指定了指針,WINDOWS還要知道該緩衝去的大小。所以您必須給出該值。
iImage 和 iSelectedImage用來指定圖象列表以及一個索引號。這樣就知道當項目被選中或沒被選中時用哪個圖象來表示該項目。像資源管理器中左邊視窗中的資料夾等小圖表就是有這兩個參數來決定的。
為了在樹型視圖中插入一個項目,您必須至少設定hParent, hInsertAfter,另外您還要設定imask和pszText值。
把圖形加到圖形視圖中如果您想要在項目的名稱左邊顯示圖示的話,您必須創建一個圖形列表,並且把它和樹形視圖相關聯起來。您可以調用ImageList_Create來創建一個圖形列表。
ImageList_Create PROTO cx:DWORD, cy:DWORD, flags:DWORD, \
cInitial:DWORD, cGrow:DWORD 如果創建成功的話,該函式返回一個空的圖象列表的句柄。
cx == 以像素為單位的圖象的寬度。
cy == 以像素為單位的圖象的高度。圖象列表中的每一幅的高度都必須相同。否則WINDOWS會對您的圖象進行裁剪,如果過大的話就可能裁剪成幾小塊。所以您必須指定相同大小的圖象。
flags == 指定圖象列表的圖象的顏色深度。詳細情況請參考WIN32 API 指南。
cInitial == 指定包含的圖象的數目。Windows將依此來分配合適的記憶體。
cGrow == 在增加新圖象是一次增加的數目。
圖象列表不是視窗。僅僅是保存在那給其它的視窗使用的一種資源。 在圖象列表產生後,您可以調用ImageList_Add來向其中加入圖象。
ImageList_Add PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD 如果該函式調用失敗的話,返回-1。
himl == 圖象列表的句柄。它是調用ImageList_Create時返回的值。
hbmImage == 加入圖象列表的點陣圖的句柄。您通常把點陣圖保存在資源中,然後調用LoadBitmap來把它載入進來。 注意您沒有必要指定該點陣圖中包含的圖象的數目。WINDOWS會根據它的大小,自動計算。
當圖象列表準備就緒後,您可以傳送訊息TVM_SETIMAGELIST給樹型視圖來讓圖象列表和樹型視圖聯繫起來。
TVM_SETIMAGELIST
wParam = 圖象列表的狀態,一共有兩種: TVSIL_NORMAL 包含被選中和沒有被選中兩種狀態的圖象。 TVSIL_STATE 包含了用戶自定義的狀態的圖象。 lParam = 圖象列表的句柄。 檢索樹型視圖的信息您可以通過傳送訊息TVM_GETITEM來檢索圖形視圖的信息。
TVM_GETITEM
wParam = 0
在傳送該訊息前必須設定成員變數imask的值,以便WINDOWS能告訴相關的信息。當然,最重要的是,您必須傳遞您想得到信息的項目的句柄。這就引起了一個問題,您如何得到項目的句柄?要保存所有項目的句柄嗎?
答案是很簡單的:沒有必要。您可以傳送訊息TVM_GETNEXTITEM到樹型視圖以檢索您想要得到其屬性的項目的句柄。譬如:您可以查詢第一個子項目的句柄、根目錄的句柄、選中的項目的句柄等等。
TVM_GETNEXTITEM
wParam = 標誌
lParam = 樹型視圖的句柄(僅僅當wParam的值是某些標誌位時才是必須的)。 wParam中的值非常重要, 我解釋如下:
TVGN_CARET 選中的項目 TVGN_CHILD hitem參數指定項目的第一個子項目 TVGN_DROPHILITE 拖-拉操作的目的項目 TVGN_FIRSTVISIBLE 第一個可見項目 TVGN_NEXT 下一個同級項目 TVGN_NEXTVISIBLE 下一個可見項目,指定的項目必須可見。傳送訊息TVM_GETITEMRECT 來決定項目是否可見 TVGN_PARENT 指定項目的父項目 TVGN_PREVIOUS 前一個同級項目 TVGN_PREVIOUSVISIBLE 前一個可見項目,指定的項目必須可見。傳送訊息TVM_GETITEMRECT 來決定項目是否可見 TVGN_ROOT 根項目 由此您可以通過傳送該訊息來得到項目的句柄,然後在傳送訊息TVM_GETITEM時在結構體變數TV_ITEM的成員變數hItem中放入該項目的句柄就可以得到關於該項目的有關信息了。
在樹型視圖中進行拖-拉操作也就是因為這一部分我才決定寫這課教程。當我按照InPrise公司的WIN32幫助來運行例子時,發現它的幫助中缺少真正重要的信息。我只有通過自己做實驗,最後總算弄明白來箇中來由。希望您不要和我一樣再去走這些彎路,下面我把我所知的在樹型視圖中進行拖-拉操作的步驟描述如下:
當用戶要拖動一個項目時,樹型視圖控制項會給它的父視窗傳送TVN_BEGINDRAG通知訊息。您可以在此處創建表示項目處在拖動操作中的圖象,這可以通過傳送TVM_CREATEDRAGIMAGE訊息給樹型視圖,讓其為目前使用的圖象產生一副預設的圖象來實現。樹型視圖控制項將創建一個圖象列表,其中僅包含一副在拖動中顯示的圖象,圖象列表創建後,您可以得到它的句柄。 在拖拉的圖象生成後,您可以通過調用ImageList_BeginDrag來指定拖動圖象的熱點位置。 ImageList_BeginDrag PROTO himlTrack:DWORD, \
iTrack:DWORD , \
dxHotspot:DWORD, \
dyHotspot:DWORD
himlTrack 是包含了拖拉時顯示的圖象的圖象列表的句柄
iTrack 是選中的圖象在圖象列表中的索引號。
dxHotspot 因為在拖動中該圖象被用來取代游標,所以我們必須指定圖象中的哪一點是游標的左上角的位置。dxHotspot是水平相對位置。
dyHotspot 是垂直相對位置。
iTrack等於0。如果您要想游標的熱點在拖拉中顯示的圖象的左上角,把dxHotspot和dyHotspot都設成0。 當拖拉的圖象要顯示時,我們調用ImageList_DragEnter 在樹型視圖中顯示該圖象。 ImageList_DragEnter PROTO hwndLock:DWORD, x:DWORD, y:DWORD
hwndLock 是進行拖拉中的視窗的句柄,拖拉的動作限制在該視窗中。
x 和 y是在拖拉時顯示圖象的初始位置的坐標值。這些值是相對於視窗的左上角而不是客戶區的左上角。 既然可以顯示拖動中的圖象了,我們就要處理拖動操作了。在這裡有一個小問題。我們監視拖動是通過監視滑鼠游標的移動來實現的,譬如在移動時我們通過捕獲WM_MOUSEMOVE訊息來得到移動中的坐標位置,通過捕獲WM_LBUTTONUP訊息來獲知用戶的放下操作。但這時如果滑鼠游標移過子視窗時父視窗就無法再得到滑鼠游標的移動以及滑鼠的按鍵訊息了。解決辦法是調用SetCapture函式了鎖定滑鼠事件,這樣無論滑鼠移到那裡和有什麼動作,我們的視窗都可以知道了。 在處理WM_MOUSEMOVE訊息時,您可以調用ImageList_DragMove來更新圖象移動的軌跡。 該函式可以移動拖放操作中的圖象位置。另外,如果您想讓移動中的圖象經過某些項目時高量度顯示,可以調用TVM_HITTEST 來確定是否經過某個項目的上面。如果是的話,您可以傳送TVM_SELECTITEM訊息並設定 TVGN_DROPHILITE標誌位使得那個項目高亮度顯示。注意:在傳送訊息TVM_SELECTITEM前,您必須先隱藏圖象列表,否則會留下非常難看的軌跡。要隱藏拖動中的圖象可以調用ImageList_DragShowNolock,在顯示完高亮度的圖象後再調用該函式以讓拖動中的圖象再正常顯示。 當用戶釋放主鍵後,您必須做幾件事。 如果您在高亮度顯示的時候釋放滑鼠主鍵(表示您想把該項目加到此處),您必須使該項目變成正常地顯示,這可以通過傳送訊息TVM_SELECTITEM訊息並設定標誌位TVGN_DROPHILITE來實現,只是這時lParam必須為0。如果您不讓高亮度顯示的項目恢復正常,那就會發生一個奇怪的現象:當您再選擇另外的項目時,那個項目的圖象會包含在一個正方形中,當時高亮度顯示的項目依舊是上一個項目。接下來必須調用ImageList_EndDrag和ImageList_DragLeave。還有調用ReleaseCapture來釋放捕獲的滑鼠。如果您創建了一個圖象列表,那還要調用calling ImageList來將它銷毀,在拖放操作結束後您可以進行另外其它的操作。
樹型視圖控制項的數據結構
1. HTREEITEM句柄
樹形控制項中的每個節點都可以由一個HTREEITEM類型的句柄表示。我們通過CTreeCtrl類的成員函式對樹進行訪問和操作時,很多時候都要用到HTREEITEM句柄。
2. TVITEM結構體
TVITEM結構體描述了樹形控制項節點的屬性,定義如下:
typedef struct tagTVITEM {
UINT mask; // 包含一些掩碼位(下面的括弧中列出)的組合,用來表明結構的哪些成員是有效的
HTREEITEM hItem; // 樹節點的句柄(TVIF_HANDLE)
UINT state; // 樹節點的狀態(TVIF_STATE)
UINT stateMask; // 狀態的掩碼組合(TVIF_STATE)
LPTSTR pszText; // 樹節點的標籤文本(TVIF_TEXT)
int cchTextMax; // 標籤文本緩衝區的大小(TVIF_TEXT)
int iImage; // 樹節點的圖像索引(TVIF_IMAGE)
int iSelectedImage; // 選中項的圖像索引(TVIF_SELECTEDIMAGE)
int cChildren; // 表明節點是否有子節點,為1則有,為0則沒有(TVIF_CHILDREN)
LPARAM lParam; // 一個32 位的附加數據(TVIF_PARAM)
} TVITEM, *LPTVITEM;
此結構體中多個元素涉及到了圖像和狀態等,有必要具體解釋下。
樹形控制項節點需要顯示圖示時,就要為樹形控制項關聯一個圖像序列,上面的iImage成員就代表了該結構體對應的樹節點的圖示在圖像序列中的索引,iSelectedImage則代表該樹節點被選中時顯示的圖示在圖像序列中的索引。
stateMask用來說明要獲取或設定樹節點的哪些狀態。下面是state和stateMask的一些常用值及含義:
state 對應的stateMask 含義
TVIS_CUT TVIS_CUT 節點被選擇用來進行剪下和貼上操作
TVIS_DROPHILITED TVIS_DROPHILITED 節點成為拖動操作的目標
TVIS_EXPANDED TVIS_EXPANDED 節點的子節點被展開
TVIS_EXPANDEDONCE TVIS_EXPANDEDONCE 節點的子節點曾經被展開過
TVIS_SELECTED TVIS_SELECTED 節點被選中
lParam在實際開發中常用來存放與樹節點有關的附加數據。
3. NMTREEVIEW結構體
NMTREEVIEW結構體中包含了樹形控制項通知訊息的相關信息。樹形控制項的大多數通知訊息都會帶有指向該結構體的指針。NMTREEVIEW結構體的定義如下:
typedef struct tagNMTREEVIEW {
NMHDR hdr; // 標準的NMHDR結構
UINT action; // 表明是用戶的什麼行為觸發了該通知訊息
TVITEM itemOld; // 原節點的屬性
TVITEM itemNew; // 新節點的屬性
POINT ptDrag; // 事件發生時滑鼠的客戶區坐標
} NMTREEVIEW, *LPNMTREEVIEW;
4. TVINSERTSTRUCT結構體
向樹形控制項中插入新節點時需要用到TVINSERTSTRUCT結構體,它常與TVM_INSERTITEM訊息一起使用。定義如下:
typedef struct tagTVINSERTSTRUCT {
HTREEITEM hParent; // 父節點的句柄
HTREEITEM hInsertAfter; // 指明插入到同層中哪一項的後面
#if (_WIN32_IE >= 0x0400)
union
{
TVITEMEX itemex;
TVITEM item;
} DUMMYUNIONNAME;
#else
TVITEM item; // 要添加的新節點的屬性
#endif
} TVINSERTSTRUCT, *LPTVINSERTSTRUCT;
若hParent成員為TVI_ROOT或NULL,那么新節點將被作為樹的根節點插入。hInsertAfter除了可以是某個節點的句柄,還可以有四種取值:TVI_FIRST(插入到樹形控制項的最前面)、TVI_LAST(插入到樹形控制項的最後面)、TVI_ROOT(作為根節點插入)和TVI_SORT(按字母順序插入)。
5. NMTVDISPINFO結構體
NMTVDISPINFO結構體中包含了與樹節點的顯示有關的信息。定義如下:
typedef struct tagNMTVDISPINFO {
NMHDR hdr;
TVITEM item;
} NMTVDISPINFO, *LPNMTVDISPINFO;
樹型視圖控制項CTreeCtrl類的主要成員函式
CImageList* SetImageList(CImageList * pImageList,int nImageListType);
如果樹節點需要顯示圖示時,則必須先創建一個CImageList類的對象,並為其添加多個圖像組成一個圖像序列,然後調用SetImageList函式為樹形控制項設定圖像序列,在用InsertItem插入節點時傳入所需圖像在圖像序列中的索引即可。後面的例子中會演示。參數pImageList為指向圖像序列類CImageList的對象的指針,若為NULL則刪除樹形控制項的所有圖像。參數nImageListType指定圖像序列的類型,可以是TVSIL_NORMAL(普通圖像序列)或TVSIL_STATE(狀態圖像序列,用圖像表示節點的狀態)。
UINT GetCount( ) const;
獲取樹形控制項中節點的數量。
DWORD_PTR GetItemData(HTREEITEM hItem) const;
獲取樹形控制項中某個指定節點的附加32位數據。參數hItem為指定的樹節點的句柄。
BOOL SetItemData(HTREEITEM hItem,DWORD_PTR dwData);
為樹形控制項中某個指定節點設定附加的32位數據。參數hItem同上,dwData為要設定的32位數據。
CString GetItemText(HTREEITEM hItem) const;
獲取樹形控制項中某個指定節點的標籤文本。參數hItem同上。返回值是包含標籤文本的字元串。
BOOL SetItemText(HTREEITEM hItem,LPCTSTR lpszItem);
為樹形控制項中某個指定節點設定標籤文本。參數hItem同上,lpszItem為包含標籤文本的字元串的指針。
HTREEITEM GetNextSiblingItem(HTREEITEM hItem) const;
獲取樹形控制項中某個指定節點的下一個兄弟節點。參數hItem同上。返回值是下一個兄弟節點的句柄。
HTREEITEM GetPrevSiblingItem(HTREEITEM hItem) const;
獲取樹形控制項中某個指定節點的上一個兄弟節點。參數hItem同上。返回值是上一個兄弟節點的句柄。
HTREEITEM GetParentItem(HTREEITEM hItem) const;
獲取樹形控制項中某個指定節點的父節點。參數hItem同上。返回值是父節點的句柄。
HTREEITEM GetRootItem( ) const;
獲取樹形控制項根節點的句柄。
HTREEITEM GetSelectedItem( ) const;
獲取樹形控制項當前選中節點的句柄。
BOOL DeleteAllItems( );
刪除樹形控制項中的所有節點。刪除成功則返回TRUE,否則返回FALSE。
BOOL DeleteItem(HTREEITEM hItem);
刪除樹形控制項中的某個節點。參數hItem為要刪除的節點的句柄。刪除成功則返回TRUE,否則返回FALSE。
HTREEITEM InsertItem(LPCTSTR lpszItem,int nImage,int nSelectedImage,HTREEITEM hParent = TVI_ROOT,HTREEITEM hInsertAfter = TVI_LAST);
在樹形控制項中插入一個新節點。參數lpszItem為新節點的標籤文本字元串的指針,參數nImage為新節點的圖示在樹形控制項圖像序列中的索引,參數nSelectedImage為新節點被選中時的圖示在圖像序列中的索引,參數hParent為插入節點的父節點的句柄,參數hInsertAfter為新節點的前一個節點的句柄,即新節點將被插入到hInsertAfter節點之後。
BOOL SelectItem(HTREEITEM hItem);
選中指定的樹節點。參數hItem為要選擇的節點的句柄。若成功則返回TRUE,否則返回FALSE。