WIN32彙編: 19.樹型視圖控件

第十九課 樹型視圖控件


本課中,我們將學習如何使用樹型視圖控件。另外還要學習如何在樹型視圖中完成拖-拉動作,以及如何使用圖象列表。

理論:

樹型視圖是一種特別的窗口,我們可以使用它一目瞭然地表示某種層次關係。譬如象在資源管理器中左邊窗口中的就是樹型視圖。您可以調用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幫助來運行例子時,發現它的幫助中缺少真正重要的信息。我只有通過自己做實驗,最後總算弄明白來個中來由。希望您不要和我一樣再去走這些彎路,下面我把我所知的在樹型視圖中進行拖-拉操作的步驟描述如下:
  1. 當用戶要拖動一個項目時,樹型視圖控件會給它的父窗口發送TVN_BEGINDRAG通知消息。您可以在此處創建表示項目處在拖動操作中的圖象,這可以通過發送TVM_CREATEDRAGIMAGE消息給樹型視圖,讓其爲目前使用的圖象產生一副缺省的圖象來實現。樹型視圖控件將創建一個圖象列表,其中僅包含一副在拖動中顯示的圖象,圖象列表創建後,您可以得到它的句柄。
  2. 在拖拉的圖象生成後,您可以通過調用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來結束拖拉操作模式。爲了使得樹形視圖控件好看,我們檢查最後高亮度顯示的項目,並且選中它。我們還必須使得其不高亮度顯示,否則其它的項目被選中時就不能高亮度顯示了。最後我們釋放對鼠標輸入事件的捕獲。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章