樹型檢視控制元件(轉)

heying1229發表於2007-07-28
樹型檢視控制元件:

  本課中,我們將學習如何使用樹型檢視控制元件。另外還要學習如何在樹型檢視中完成拖-拉動作,以及如何使用圖象列表。
理論:
樹型檢視是一種特別的視窗,我們可以使用它一目瞭然地表示某種層次關係。譬如象在資源管理器中左邊視窗中的就是樹型檢視。您可以呼叫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 masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
include masm32includecomctl32.inc
include masm32includegdi32.inc
includelib masm32libgdi32.lib
includelib masm32libcomctl32.lib
includelib masm32libuser32.lib
includelib masm32libkernel32.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來結束拖拉操作模式。為了使得樹形檢視控制元件好看,我們檢查最後高亮度顯示的專案,並且選中它。我們還必須使得其不高亮度顯示,否則其它的專案被選中時就不能高亮度顯示了。最後我們釋放對滑鼠輸入事件的捕獲。

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10172717/viewspace-928733/,如需轉載,請註明出處,否則將追究法律責任。

相關文章