本課中,我們將學習如何使用樹型視圖控件。另外還要學習如何在樹型視圖中完成拖-拉動作,以及如何使用圖象列表。
理論:
樹型視圖是一種特別的窗口,我們可以使用它一目了然地表示某種層次關系。譬如象在資源管理器中左邊窗口中的就是樹型視圖。您可以調用CreateWindowEx來創建樹型視圖,傳遞一個類名“"SysTreeView32"”,或者您也可以把它放到一個對話框中去。不要忘了在您的代碼中加入InitCommonControls函數。
樹型視圖有幾種特有的風格。下面是幾種經常使用的。
TVS_HASBUTTONS == 在父項目中顯示(+)或(-)。用戶可以通過點擊該符號來展開或收起該父項目下的子項目。如果想在根目錄下也有這個符號必須指定TVS_LINESATROOT風格。
TVS_HASLINES == 在層次中用線條來連接各個項目名稱。
TVS_LINESATROOT == 在根目錄下的項目也用線連接。如果沒有指定TVS_HASLINES風格,該風格也就會被忽略。
像其它的通用控件一樣,樹型視圖用消息來完成通信。父窗口發送一系列的消息給樹型視圖,而樹型視圖發送"notification"消息給它的父窗口。在這方面,樹型視圖和其它的通用控制沒什麼兩樣。
當有事件發生時,樹型視圖發送一個WM_NOTIFY消息個父窗口,並在消息中附帶傳遞一些附加信息。
WM_NOTIFY
wParam ==控件的ID。因為該值不是唯一的,故我們不用它。我們使用NMHDR結構體中的hwndFrom或IDFrom成員變量。
lParam == 指向NMHDR結構體的指針。有一些控件可能傳遞一個指向更大一點的結構體的指針。但該結構體必須保證它的第一個成員變量是一個NMHDR型的變量。這樣,您在處理lParam變量時,至少可以得到一個NMHDR型的變量。
下面我們來看NMHDR:
NMHDR struct DWORD
hwndFrom DWORD ?
idFrom DWORD ?
code DWORD ?
NMHDR ends
hwndFrom是發送WM_NOTIFY消息的控件的窗口句柄。
idFrom是發送WM_NOTIFY消息的控件的ID。
code是控件發送給父窗口的數據。
樹型視圖發送給父窗口的通知消息以TVN_打頭。 樹型視圖接收到的消息以TVM_打頭,譬如:TVM_CREATEDRAGIMAGE。 樹型視圖發送TVN_XXX消息時在code變量中放入NMHDR型變量。父窗口發送TVM_消息來控制樹型視圖。
在樹型視圖中加入項目
在創建完樹型視圖後可以通過發送TVM_INSERTITEM消息往其中加入項目了。
TVM_INSERTITEM
wParam = 0;
lParam = 指向結構體TV_INSERTSTRUCT的指針;
您應當知道一些關於樹型視圖中的項目之間關系的一些術語。 一個項目可能是一個父親、兒子或兩者都是。父項目下含有子項目,而該父項目又有可能是其它項目的子項目。一個沒有父項目的項目叫根項目。在樹型視圖中可能有多個根項目。現在我們來看看TV_INSERTSTRUCT結構體:
TV_INSERTSTRUCT STRUCT DWORD
hParent DWORD ?
hInsertAfter DWORD ?
ITEMTYPE <>
TV_INSERTSTRUCT ENDS
hParent = 父項目的句柄。如果該值為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,該結構體用來填充關於選定項目的信息。
imask 用來指定TV_ITEM的那些成員變量有效。譬如,如果指定了TVIF_TEXT,這意味著pszText成員變量是有效的。您可以同時指定幾個標志位。
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 == 指定包含的圖象的數目。WINDWOS將依此來分配合適的內存。
cGrow == 在增加新圖象是一次增加的數目。
圖象列表不是窗口。僅僅是保存在那給其它的窗口使用的一種資源。 在圖象列表產生後,您可以調用ImageList_Add來向其中加入圖象。
ImageList_Add PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD
如果該函數調用失敗的話,返回-1。
himl == 圖象列表的句柄。它是調用ImageList_Create時返回的值。
hbmImage == 加入圖象列表的位圖的句柄。您通常把位圖保存在資源中,然後調用LoadBitmap來把它加載進來。 注意您沒有必要指定該位圖中包含的圖象的數目。WINDOWS會根據它的大小,自動計算。
hbmMask == 掩碼位圖的句柄。如果沒有使用掩碼位圖,可以忽略該值。 通常我們加入兩種圖象到圖象列表中。一種時被選中時顯示的圖象,另一種時沒被選中時顯示的。
當圖象列表准備就緒後,您可以發送消息TVM_SETIMAGELIST給樹型視圖來讓圖象列表和樹型視圖聯系起來。
TVM_SETIMAGELIST
wParam = 圖象列表的狀態,一共有兩種:
TVSIL_NORMAL 包含被選中和沒有被選中兩種狀態的圖象。
TVSIL_STATE 包含了用戶自定義的狀態的圖象。
lParam = 圖象列表的句柄。
檢索樹型視圖的信息
您可以通過發送消息TVM_GETITEM來檢索圖形視圖的信息。
TVM_GETITEM
wParam = 0
lParam =指向結構體TV_ITEM的指針。該結構體將用來得到相關的信息。
在發送該消息前必須設置成員變量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來將它銷毀,在拖放操作結束後您可以進行另外其它的操作。
例子代碼:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
.const
IDB_TREE equ 4006 ; ID of the bitmap resource
.data
ClassName db "TreeViewWinClass",0
AppName db "Tree View Demo",0
TreeViewClass db "SysTreeView32",0
Parent db "Parent Item",0
Child1 db "child1",0
Child2 db "child2",0
DragMode dd FALSE ; a flag to determine if we are in drag mode
.data?
hInstance HINSTANCE ?
hwndTreeView dd ? ; handle of the tree view control
hParent dd ? ; handle of the root tree view item
hImageList dd ? ; handle of the image list used in the tree view control
hDragImageList dd ? ; handle of the image list used to store the drag image
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
invoke ExitProcess,eax
invoke InitCommonControls
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_APPWORKSPACE
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
CW_USEDEFAULT,200,400,NULL,NULL,\
hInst,NULL
mov hwnd,eax
.while TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endw
mov eax,msg.wParam
ret
WinMain endp
WndProc proc uses edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL tvinsert:TV_INSERTSTRUCT
LOCAL hBitmap:DWORD
LOCAL tvhit:TV_HITTESTINFO
.if uMsg==WM_CREATE
invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\
WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+TVS_LINESATROOT,0,\
0,200,400,hWnd,NULL,\
hInstance,NULL ; Create the tree view control
mov hwndTreeView,eax
invoke ImageList_Create,16,16,ILC_COLOR16,2,10 ; create the associated image list
mov hImageList,eax
invoke LoadBitmap,hInstance,IDB_TREE ; load the bitmap from the resource
mov hBitmap,eax
invoke ImageList_Add,hImageList,hBitmap,NULL ; Add the bitmap into the image list
invoke DeleteObject,hBitmap ; always delete the bitmap resource
invoke SendMessage,hwndTreeView,TVM_SETIMAGELIST,0,hImageList
mov tvinsert.hParent,NULL
mov tvinsert.hInsertAfter,TVI_ROOT
mov tvinsert.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE
mov tvinsert.item.pszText,offset Parent
mov tvinsert.item.iImage,0
mov tvinsert.item.iSelectedImage,1
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
mov hParent,eax
mov tvinsert.hParent,eax
mov tvinsert.hInsertAfter,TVI_LAST
mov tvinsert.item.pszText,offset Child1
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
mov tvinsert.item.pszText,offset Child2
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
.elseif uMsg==WM_MOUSEMOVE
.if DragMode==TRUE
mov eax,lParam
and eax,0ffffh
mov ecx,lParam
shr ecx,16
mov tvhit.pt.x,eax
mov tvhit.pt.y,ecx
invoke ImageList_DragMove,eax,ecx
invoke ImageList_DragShowNolock,FALSE
invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit
.if eax!=NULL
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,eax
.endif
invoke ImageList_DragShowNolock,TRUE
.endif
.elseif uMsg==WM_LBUTTONUP
.if DragMode==TRUE
invoke ImageList_DragLeave,hwndTreeView
invoke ImageList_EndDrag
invoke ImageList_Destroy,hDragImageList
invoke SendMessage,hwndTreeView,TVM_GETNEXTITEM,TVGN_DROPHILITE,0
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_CARET,eax
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,0
invoke ReleaseCapture
mov DragMode,FALSE
.endif
.elseif uMsg==WM_NOTIFY
mov edi,lParam
assume edi:ptr NM_TREEVIEW
.if [edi].hdr.code==TVN_BEGINDRAG
invoke SendMessage,hwndTreeView,TVM_CREATEDRAGIMAGE,0,[edi].itemNew.hItem
mov hDragImageList,eax
invoke ImageList_BeginDrag,hDragImageList,0,0,0
invoke ImageList_DragEnter,hwndTreeView,[edi].ptDrag.x,[edi].ptDrag.y
invoke SetCapture,hWnd
mov DragMode,TRUE
.endif
assume edi:nothing
.elseif uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
end start
分析:
在處理消息WM_CREATE的代碼中,您可以創建樹型視圖控件。
invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\
WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+TVS_LINESATROOT,0,\
0,200,400,hWnd,NULL,\
hInstance,NULL
注意: TVS_xxxx 是樹型視圖所特有的風格。
invoke ImageList_Create,16,16,ILC_COLOR16,2,10
mov hImageList,eax
invoke LoadBitmap,hInstance,IDB_TREE
mov hBitmap,eax
invoke ImageList_Add,hImageList,hBitmap,NULL
invoke DeleteObject,hBitmap
invoke SendMessage,hwndTreeView,TVM_SETIMAGELIST,0,hImageList
接下來,您可以創建一個空的圖像列表,該圖像列表容納的是以像素為單位16x16大小和16位深度的圖像,該圖像列表初始包含兩幅圖像,最大可以容納10幅。然後我們從資源中加載圖像,並把它們放到圖像列表中去。隨後我們刪除掉圖像的句柄,因為我們不需要再用到它。設置好圖像列表後,我們通過發送消息TVM_SETIMAGELIST把它和樹型視圖控件聯系起來。
mov tvinsert.hParent,NULL
mov tvinsert.hInsertAfter,TVI_ROOT
mov tvinsert.u.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE
mov tvinsert.u.item.pszText,offset Parent
mov tvinsert.u.item.iImage,0
mov tvinsert.u.item.iSelectedImage,1
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
現在把項目插入到樹型視圖控件中去,首先我們從根項目開始。因為是根項目,所以成員變量hParent是NULL,hInsertAfter是TVI_ROOT。imask指定TV_ITEM結構體變量中的pszText,iImage和iSelectedImage三個成員變量的值是有效的。我們應該給這三個成員變量賦上正確的值。其中pszText顯示項目的名稱,iImage是圖像列表中圖像的索引號,該圖像顯示在未選中的項目名稱的左邊,iSelectedImage是選中的項目的圖像索引號。設置好了這些值後,我們發送TVM_INSERTITEM消息給樹型視圖控件來把根項目加入到樹型視圖控件中去。
mov hParent,eax
mov tvinsert.hParent,eax
mov tvinsert.hInsertAfter,TVI_LAST
mov tvinsert.u.item.pszText,offset Child1
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
mov tvinsert.u.item.pszText,offset Child2
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
加入完根項目後,我們再加入子項目。這時的成員變量hParent是其父項目的句柄,hInsertAfter的值是TVI_LAST。至於選中和非選中時用的圖像是一樣的,所以我們無需更改其它變量的值。
.elseif uMsg==WM_NOTIFY
mov edi,lParam
assume edi:ptr NM_TREEVIEW
.if [edi].hdr.code==TVN_BEGINDRAG
invoke SendMessage,hwndTreeView,TVM_CREATEDRAGIMAGE,0,[edi].itemNew.hItem
mov hDragImageList,eax
invoke ImageList_BeginDrag,hDragImageList,0,0,0
invoke ImageList_DragEnter,hwndTreeView,[edi].ptDrag.x,[edi].ptDrag.y
invoke SetCapture,hWnd
mov DragMode,TRUE
.endif
assume edi:nothing
當用戶拖動項目時,樹型視圖控件將發送WM_NOTIFY消息給它的父窗口,子消息號是TVN_BEGINDRAG。在lPAram中是指向結構體NM_TREEVIEW 的指針,該結構體包含了一些附加信息。我們把lParam的值放到edi寄存器中,這樣就可以把edi作為一個指針來使用。“assume edi:ptr NM_TREEVIEW ”語句用來告訴編譯器MASM把edi作為指向NM_TREEVIEW 的結構體的變量使用。我們通過發送消息TVM_CREATEDRAGIMAGE來創建一個拖動的圖像。它將返回一個新創建的圖像列表的句柄,該圖像列表中包含拖動中的圖像。我們調用ImageList_BeginDrag函數設置拖動圖像的熱點。調用ImageList_DragEnter函數進入操作。該函數會在特定位置顯示拖動中的圖像。起初顯示的位置我們設在結構體NM_TREEVIEW中的成員變量ptDrag所指的位置。我們鎖定鼠標的輸入,並設置標志變量,表示我們進入了拖拉操作。
.elseif uMsg==WM_MOUSEMOVE
.if DragMode==TRUE
mov eax,lParam
and eax,0ffffh
mov ecx,lParam
shr ecx,16
mov tvhit.pt.x,eax
mov tvhit.pt.y,ecx
invoke ImageList_DragMove,eax,ecx
invoke ImageList_DragShowNolock,FALSE
invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit
.if eax!=NULL
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,eax
.endif
invoke ImageList_DragShowNolock,TRUE
.endif
現在我們來看看WM_MOUSEMOVE消息的處理過程。當用戶拖動圖像時,我們的父窗口將接收到WM_MOUSEMOVE。為了響應這些消息,我們調用ImageList_DragMove來更新更新圖像的位置。然後我們發送消息TVM_HITTEST給列表視圖控件看看拖拉中的圖像是否正好經過某些項目的上面,當然還要附帶傳遞坐標位置等信息。如果經過的話,我們發送消息TVM_SELECTITEM並附帶TVGN_DROPHILITE標志給樹型視圖控件,後者將會高亮度顯示正被經過的項目。在高亮度顯示的過程中,我們隱藏掉拖動中的圖像免得顯示的圖像難看。
.elseif uMsg==WM_LBUTTONUP
.if DragMode==TRUE
invoke ImageList_DragLeave,hwndTreeView
invoke ImageList_EndDrag
invoke ImageList_Destroy,hDragImageList
invoke SendMessage,hwndTreeView,TVM_GETNEXTITEM,TVGN_DROPHILITE,0
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_CARET,eax
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,0
invoke ReleaseCapture
mov DragMode,FALSE
.endif
當用戶釋放鼠標左鍵後,拖拉操作就可以結束了。我們調用ImageList_DragLeave,ImageList_EndDrag和ImageList_Destroy來結束拖拉操作模式。為了使得樹形視圖控件好看,我們檢查最後高亮度顯示的項目,並且選中它。我們還必須使得其不高亮度顯示,否則其它的項目被選中時就不能高亮度顯示了。最後我們釋放對鼠標輸入事件的捕獲。