Tutorial 19: Tree View Control

このチュートリアルでは、ツリービューとイメージリストの使い方、 さらにツリービューへのドラッグアンドドロップの方法とを学ぶ。
 ソース   リソース   ビットマップ   実行結果 

※  インクルードファイルによっては、このままではコンパイルが通らないので、 各自の windows.inc ファイルを見て、TVITEMA 構造体で宣言されているメンバ変数が _mask なら ソースファイル中の imask を _mask に変更すること。

Theory:

ツリービューコントロールというのは特殊なウィンドウで、オブジェクトを階層にして表示する。 すぐ思いつく例はエクスプローラの左側のペインだ。 このコントロールはオブジェクトの関連を表すのによく使用される。

このツリービューコントロールはCreateWindowEx関数の引数であるクラス名に"SysTreeView32"を指定してCALLするか、 ダイアログボックスに取り込むかで表示できる。 ただしこの時、InitCommonControls関数を呼び出すコードを忘れてはならない。

ツリービューコントロールにはツリービュー独特なスタイルがいくつかあるので、説明しよう。

ツリービューコントロールは他のコモンコントロールと同様、 親ウィンドウと、メッセージをやりとりすることによりコミュニケーションをとる。 親ウィンドウは様々なメッセージをツリービューに送信することができ、 ツリービューは親ウィンドウに「通知」メッセージを送信できる。 この点においても、ツリービューコントロールは他のコモンコントロールと同じである。

何かあれば、情報を付加してWM_NOTIFYメッセージを送信する。

WM_NOTIFY
  • wParam = コントロールID
    この値の唯一性は保証されないので使用しない。代わりに、lParamがNMHDR構造体へのポインタとなっているのだが、そのメンバであるhwndFromとIDFromを使用する。
  • lParam = NMHDR構造体へのポインタ
    いくつかのコントロールにはより大きな構造体へのポインタになってるかもしれないが、最初のメンバとして、NMHDR構造体を持っていなければならない。つまり、lParamは少なくともNMHDR構造体へのポインタであることは確かである。

次に、NWHDR構造体についてみてみることにしよう。

NMHDR struct DWORD
   hwndFrom   DWORD ?
   idFrom     DWORD ?
   code       DWORD ?
NMHDR ends

hwndFromはWM_NOTIFYメッセージを送信するコントロールのウィンドウ ハンドルで、idFromはそのコントロールのIDである。 codeは親ウィンドウに送信したいメッセージである。 ツリービュー通知は「TVN_」、ツリービューメッセージは「TVM_」から始まることになっている。 ツリービューコントロールはNMHDR構造体のメンバ変数codeにTVN_xxxxをセットし、 親ウィンドウはコントロールにTVM_xxxxを送信する。

●ツリービューコントロールにアイテムを追加

ツリービューコントロールを作成したら、 TVM_INSERTITEMメッセージを送信してアイテムが追加できるようになる。

TVM_INSERTITEM
  • wParam = 0
  • lParam = 既にメンバ変数の値がセットされているTV_ITEM構造体へのポインタ

ツリービューコントロールにおけるアイテム間の関係についていくつかの用語を覚えておかなければならない。 アイテムは、同時に親と子、どちらでもなれる。親アイテムは関連したサブアイテムを持つアイテムのことで、 またそのアイテムは同時に、他のアイテムの子アイテムにもなれる。 親アイテムのないアイテムのことをルートアイテムと呼ぶことになる。 ツリービューコントロールにおいて、ルートアイテムは1つだけでなく、複数存在しうる。 では、TV_INSERTSTRUCT構造体について見ていこう。

TV_INSERTSTRUCT STRUCT DWORD
 hParent      DWORD ?
 hInsertAfter DWORD ?
 ITEMTYPE     <>
TV_INSERTSTRUCT ENDS

TVITEMしかここでは使用しないので、TVITEMの説明を行う。

※  以下の説明は、TV_ITEM構造体となっているが、 たぶん windows.inc ファイルの TVITEMA 構造体の説明をしているのだと思われる。 そこらへん、各自のインクルードファイルに従って読み替えてください。(^^;

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メッセージの場合は選択されているツリービューアイテムについての情報がセットされている。

●ツリービューコントロールにイメージを追加

ツリービューのアイテムラベルの左側に画像を貼り付けたければ、イメージリストを作成し、 ツリービューコントロールに関連付けなければならない。 ImageList_Create関数をCALLすることにより作成できる。

ImageList_Create PROTO cx:DWORD, cy:DWORD, flags:DWORD, cInitial:DWORD, cGrow:DWORD

作成すれば、空のイメージリストへのハンドルが返ってくる。

イメージリストはウィンドウではない!他のウィンドウによって使用される画像の確保場所だ。 イメージリストが作成されれば、ImageList_Add関数によりイメージを追加できる。

ImageList_Add PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD

この関数は失敗すれば -1 が返ってくる。

通常、ツリービューコントロールを使用する際、イメージリストに2つのイメージだけを追加する。 1つは、ツリービューアイテムが非選択状態のときの表示されるもので、もう1つは選択状態のときに表示されるものだ。お

イメージリストの準備が整えば、TVM_SETIMAGELISTメッセージをツリービューコントロールに送信し、 イメージリストとツリービューコントロールを関連付ける。

TVM_SETIMAGELIST
  • wParam = イメージリストのタイプ
    以下の2つを選択できる。
    • TVSIL_NORMAL
      通常のイメージリストであることを示す。ツリービューアイテムは選択されたイメージと非選択状態のイメージがある
    • TVSIL_STATE
      状態付きイメージリストであることを示す。ユーザが定義した状態で表示されるツリービューアイテムのイメージがある。
  • lParam = イメージリストのハンドル

●ツリービューアイテムについての情報を取得

TVM_GETITEMメッセージを送信することにより、 ツリービューアイテムについての情報を取得できる。

TVM_GETITEM
  • wParam = 0
  • lParam = 既に値がセットされたTV_ITEM構造体へのポインタ

このメッセージを送信する前に、TV_ITEMのどのメンバをWindowsにセットしてほしいかを表すために、 imaskフラグを設定しなければならない。 そして、最も重要なことだが、hItemも取得したいアイテムのハンドルに設定しなければならない。 しかし、ここで問題がある。 どのようにして、その取得したいアイテムのハンドルを知ることができるのか? ツリービューの全てのハンドルををどこかに保存しておく必要があるのか?

ところが、解決方法は至ってシンプルである。ツリービューコントロールにTVM_GETNEXTITEMを送信することにより、 指定した属性を持ったツリービューアイテムのハンドルを取得することができる。 属性というのは、最初の子アイテム、ルートアイテム、選択されたアイテム、といったものだ。

TVM_GETNEXTITEM
  • wParam = フラグ
  • lParam = (フラグと一致した)ツリービューアイテムのハンドル

wParamの値は非常に重要なので、以下に全フラグを紹介しよう。

これらのフラグを指定して、自分の取得したいアイテムのハンドルを取得する。 SendMessage関数の戻り値は、成功すればツリービューアイテムのハンドルとなる。 そして、TVM_GETITEMメッセージを使用するために、 その返ってきたハンドルを、TV_ITEM構造体のメンバであるhItemにセットする。

●ツリービューコントロールのドラッグアンドドロップ操作

このパートこそが、今回このチュートリアルを書き上げることになった最大の理由だ。 Win32APIリファレンスにおける例を使おうかな、とは思ったのだが、重要な情報が不足していたため、 フラストレーションがたまり、やめてしまった。 試行錯誤のあと、ようやくツリービューコントロールにおけるドラッグアンドドロップの実装方法が判明した。 誰かが同じ轍を踏まないために、以下にドラッグアンドドロップの説明を行う。

  1. ユーザがアイテムをドラッグしようとしたら、ツリービューコントロールはTVN_BEGINDRAG通知を親ウィンドウに送信する。この時に、ドラッグ中に表示されるドラッグイメージを作成する。
    ツリービューコントロールにTVM_CREATEDRAGIMAGEを送信して、現在ドラッグ中のアイテムに用いられているイメージからドラッグイメージを作成しましたよ、という通知を行う。
    ツリービューコントロールは1つのドラッグイメージに対してイメージリストを作成し、そのイメージリストへのハンドルを返す。
  2. ドラッグイメージを作成し終えたら、ImageList_BeginDrag関数をCALLしてドラッグイメージのホットスポット(Windowsはこの座標をイメージの座標として認識する)を特定する。

    ImageList_BeginDrag PROTO himlTrack:DWORD, iTrack:DWORD, dxHotspot:DWORD, dyHotspot:DWORD

    • himlTrackはドラッグイメージを持っているイメージリストへのハンドル
    • iTrackはドラッグイメージとして表示するイメージリスト内のインデックス
    • dxHotspotはドラッグイメージのホットスポットの水平方向座標値で左端を原点として指定して、このイメージはマウスカーソルの代わりに使用されるので、イメージのどの部分がホットスポットになるかを指定しなければならない。
    • dyHotspotはdxHotspotと同じく、垂直方向の座標値を指定する。ツリービューコントロールにドラッグイメージを自前で作成するということを知らせておけば、通常、iTrackは0である。
      また、ドラッグイメージの左上隅をホットスポットの座標値にしたければ、dxHotspotもdyHotspotも0にすればよい。
  3. ドラッグイメージを表示する準備が整えば、ImageList_DragEnter関数をCALLして、ウィンドウ内にドラッグイメージを表示させる。

    ImageList_DragEnter PROTO hwndLock:DWORD, x:DWORD, y:DWORD

    • hwndLockはドラッグイメージを表示するウィンドウへのハンドルで、ドラッグイメージはウィンドウ外に移動できない。
    • x と yはドラッグイメージが初期状態で表示されている場所のX、Y座標を表す。これはウィンドウの左上が原点となっており、クライアントエリアが基準でないので注意すること
  4. そうすると、ドラッグイメージがウィンドウに表示されるようになるが、ツリービューコントロール内のドラッグ操作のサポートをしなければならない。でも、それほど難しいことではない。WM_MOUSEMOVEメッセージでドラッグされる経路とWM_LBUTTONUPメッセージでドロップされる座標値を監視しなければならない。
    これはSetCapture関数によりマウス入力をキャプチャーすることで、監視できるようになる。この関数をCALLすれば、マウスカーソルがどこにあろうと、指定されたウィンドウに直接、マウスメッセージがとんでくるようになる。
  5. WM_MOUSEMOVEを扱うところで、ImageList_DragMove関数をCALLしてドラッグ経路を更新していく。この関数により、ドラッグアンドドロップ操作中にドラッグされているイメージを移動できる。
    さらに、ドラッグイメージがアイテムと重なった時には、それをチェックするためのTVM_HITTESTを送信することにより、そのアイテムをハイライトさせることができる。
    ただし、TVM_SELECTITEMメッセージを送信する前に、先にドラッグイメージを隠しておかないと、ドラッグイメージの跡が汚く残ってしまうので、注意すること。
    なので、ImageList_DragShowNolock関数をCALLしてドラッグイメージを隠し、ハイライト処理が終われば、ImageList_DragShowNolock関数をCALLして、再度ドラッグイメージを表示できるようになる。
  6. ユーザがドラッグ中に左マウスボタンを離すと、やらないといけないことがいくつか発生する。もしアイテムをハイライトさせていれば、TVM_SELECTITEMメッセージをTVGN_DROPHILITEフラグ付きで再度送信してハイライトをオフにしなければならない。ただし、この時、lParam は0にセットしておくこと。
    もしハイライトをオフにしないと、他のアイテムを選択したとき、そのアイテムは矩形で囲まれて選択状態になるのだが、その時に、ハイライトされていたアイテムがまだハイライトした状態のままになっているのである。
    次に、ImageList_DragLeave関数、ImageList_EndDrag関数と順にCALLし、ReleaseCapture関数により、マウス監視をストップする。
    イメージリストを作成していたら、ImageList_Destroy関数をCALLすることによりイメージリストを削除する。
    その後、ドラッグアンドドロップ操作が終わったので、通常の処理を続けることができるようになる。

Code sample:

.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

Analysis:

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

次に、16 x 16 ピクセルで、色数16ビットの画像用のイメージリストを作成する。 この例では2つの画像をリストできるのだが、必要なら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は選択されたときに何番目に表示するかを指定する。 全てのメンバを設定すれば、TVB_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メンバは親アイテムのハンドルになっており、 使用したいイメージは同一のものなので、iImage と iSelectedImage はそのままにしておく。

.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

今度は、ユーザがアイテムをドラッグしてきた時、 ツリービューコントロールは、TVN_BEGINDRAGと共に、WM_NOTIFYメッセージを送信する。 その際、lParamは必要不可欠なNM_TREEVIEW構造体へのポインタとなっており、 そのポインタの値をediレジスタにセットし、 以降、ediレジスタをポインタ代わりに使用する。 assume edi:ptr NM_TREEVIEW という行は、 NM_TREEVIEW 構造体へのポインタとしてediレジスタを使用する、ということを表している。 今度は、ツリービューコントロールにTVM_CREATEDRAGIMAGEメッセージを送信することにより、 ドラッグ中に表示するイメージを作成する。 これにより、ドラッグイメージ付きのイメージリストのハンドルを取得し、 そのハンドルを引数にして、ImageList_BeginDrag関数をCALLすることにより、 ドラッグイメージのホットスポットを作成する。 そして、ImageList_DragEnter関数をCALLしてドラッグ操作を行うようになる。 この関数は指定したウィンドウと、ウィンドウの座標値にドラッグイメージを表示する。 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関数をCALLして、ドラッグモードも終了する。 ツリービューアイテムをカッコよく見せるには、 最後にハイライトしたアイテムをチェックして、選択し、 ハイライトを解除しなければならない。 そうしないと、他のアイテムが選択されたときにハイライトしなくなってしまう。 そして最後に、マウスのキャプチャを終了する。


[戻る]