このチュートリアルでは、ツリービューとイメージリストの使い方、 さらにツリービューへのドラッグアンドドロップの方法とを学ぶ。
ソース | リソース | ビットマップ | 実行結果 |
※ インクルードファイルによっては、このままではコンパイルが通らないので、 各自の windows.inc ファイルを見て、TVITEMA 構造体で宣言されているメンバ変数が _mask なら ソースファイル中の imask を _mask に変更すること。
|
ツリービューコントロールというのは特殊なウィンドウで、オブジェクトを階層にして表示する。 すぐ思いつく例はエクスプローラの左側のペインだ。 このコントロールはオブジェクトの関連を表すのによく使用される。
このツリービューコントロールはCreateWindowEx関数の引数であるクラス名に"SysTreeView32"を指定してCALLするか、 ダイアログボックスに取り込むかで表示できる。 ただしこの時、InitCommonControls関数を呼び出すコードを忘れてはならない。
ツリービューコントロールにはツリービュー独特なスタイルがいくつかあるので、説明しよう。
- TVS_HASBUTTONS
[+](展開前)と[-](展開前)ボタンがあり、ユーザはこれらのボタンを押して、子アイテムがあればそれを展開したり、しまいこんだりできる。ツリービューのルートアイテム(一番TOPのアイテム)を子アイテム付きのボタンにしたければ、TVS_LINESATROOT も合わせて指定しなければならない。- TVS_HASLINES
親子関係を線で結ぶ。- TVS_LINESATROOT
ツリービューコントロールのルートアイテムと、その子アイテムの間を線で結ぶときに使用する。この値は、TVS_HASLINESが指定されていないと無効になる。ツリービューコントロールは他のコモンコントロールと同様、 親ウィンドウと、メッセージをやりとりすることによりコミュニケーションをとる。 親ウィンドウは様々なメッセージをツリービューに送信することができ、 ツリービューは親ウィンドウに「通知」メッセージを送信できる。 この点においても、ツリービューコントロールは他のコモンコントロールと同じである。
何かあれば、情報を付加して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 endshwndFromは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
- hParent
親アイテムへのハンドル。これがTVI_ROOT、もしくはNULLなら、そのアイテムはツリービューコントロールの一番トップに位置する。- hInsertAfter
新たにアイテムが挿入されるアイテムへのハンドル、もしくは以下の値のうちどれか。
- TVI_FIRST
リストの一番最初にアイテムを挿入する- TVI_LAST
リストの一番最後にアイテムを挿入する- TVI_SORT
アルファベット順にリストにアイテムを挿入する- ITEMTYPE
以下の共用体である。
ITEMTYPE UNION itemex TVITEMEX <> item TVITEM <> ITEMTYPE ENDSTVITEMしかここでは使用しないので、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メッセージの場合は選択されているツリービューアイテムについての情報がセットされている。
- imaskはTV_ITEM構造体で有効なメンバがどれかを指定する。例えば、TVIF_TEXTだったら、メンバpszTextだけが有効になる。この値は複数指定可能である。
- hItemはツリービューアイテムへのハンドルである。各アイテムは、ウィンドウハンドルのように独自のハンドルを持っており、アイテムに対して何か操作したければ、このハンドルによりどのアイテムかを指定する。
- pszTextはツリービューアイテムのラベル名
- cchTextMaxはツリービューアイテムのラベル名が欲しいときにだけしか使用しない。pszTextにバッファへのポインタを格納するのだが、Windowsはそのバッファのサイズがわからないので、バッファのサイズをここに指定する。
- iImageとiSelectedImageはイメージリストのインデックスで、iImageはそのアイテムが選択されていないきに、iSelectedImageは選択されているときに表示されるイメージとなっている。エクスプローラーで言えば、左側のペインのディレクトリ画像で、選択されていなければディレクトリは閉じており、選択されているときはオープンされている。
●ツリービューコントロールにイメージを追加
ツリービューのアイテムラベルの左側に画像を貼り付けたければ、イメージリストを作成し、 ツリービューコントロールに関連付けなければならない。 ImageList_Create関数をCALLすることにより作成できる。
ImageList_Create PROTO cx:DWORD, cy:DWORD, flags:DWORD, cInitial:DWORD, cGrow:DWORD作成すれば、空のイメージリストへのハンドルが返ってくる。
- cx
イメージリスト内のイメージのピクセル幅- cy
イメージのピクセル高さで、イメージリスト内の全イメージは同サイズでなければならない。もし大きなビットマップを使用すると、Windowsはcxとcyのサイズに「細切れ」にする。なので、同一サイズで分割されるものとして、画像を準備する必要がある。- flags
色に関するフラグでモノクロにするか、何ビットにするかなどを指定するフラグ。詳細はWin32APIリファレンスを参照のこと。- cInitial
初期状態でいくつイメージがあるかを表す。Windowsはこの変数の値によりメモリ確保する。- cGrow
新しいイメージの場所を確保するためにイメージリストのサイズを拡張する際、どれだけの数のイメージを確保できるようにイメージリストを拡張するかを指定する。イメージリストはウィンドウではない!他のウィンドウによって使用される画像の確保場所だ。 イメージリストが作成されれば、ImageList_Add関数によりイメージを追加できる。
ImageList_Add PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORDこの関数は失敗すれば -1 が返ってくる。
- himl
イメージを追加したいイメージリストのハンドルで、ImageList_Create関数の呼び出しが成功したときの返り値。- hbmImage
イメージリストに追加したいビットマップのハンドル。たいてい、リソースにビットマップを格納しておき、LoadBitmap関数をCALLしてロードする。このビットマップにいくつイメージがあるかは記述しなくてもよい。なぜなら、ImageList_Create関数の引数にあるcxとcyから推測できるからである。- hbmMask
マスク付きのビットマップのハンドルで、イメージリストでマスクが使用されていなければ、これは無視される。通常、ツリービューコントロールを使用する際、イメージリストに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の値は非常に重要なので、以下に全フラグを紹介しよう。
- TVGN_CARET
現在選択されているアイテムを取得する- TVGN_CHILD
hitemで指定されたアイテムの一番最初の子アイテムを取得する- TVGN_DROPHILITE
ドラッグアンドドロップ操作のターゲットとなるアイテムを取得する- TVGN_FIRSTVISIBLE
一番最初に表示されているアイテムを取得する- TVGN_NEXT
次の兄弟関係にあるアイテムを取得する- TVGN_NEXTVISIBLE
指定されたアイテムの次に表示されているアイテムを取得する。指定するアイテムは表示されているものでなければならない。TVM_GETITEMRECTメッセージを送信して、アイテムが表示されているかどうか確認できる。- TVGN_PARENT
指定されたアイテムの親アイテムを取得する- TVGN_PREVIOUS
1つ前の兄弟関係にあるアイテムを取得する- TVGN_PREVIOUSVISIBLE
指定されたアイテムの前にくる最初の表示されているアイテムを取得する。指定するアイテムは表示されているものでなければならない。- TVGN_ROOT
ツリービューコントロールの一番トップにあるアイテムを取得する。これらのフラグを指定して、自分の取得したいアイテムのハンドルを取得する。 SendMessage関数の戻り値は、成功すればツリービューアイテムのハンドルとなる。 そして、TVM_GETITEMメッセージを使用するために、 その返ってきたハンドルを、TV_ITEM構造体のメンバであるhItemにセットする。
●ツリービューコントロールのドラッグアンドドロップ操作
このパートこそが、今回このチュートリアルを書き上げることになった最大の理由だ。 Win32APIリファレンスにおける例を使おうかな、とは思ったのだが、重要な情報が不足していたため、 フラストレーションがたまり、やめてしまった。 試行錯誤のあと、ようやくツリービューコントロールにおけるドラッグアンドドロップの実装方法が判明した。 誰かが同じ轍を踏まないために、以下にドラッグアンドドロップの説明を行う。
- ユーザがアイテムをドラッグしようとしたら、ツリービューコントロールはTVN_BEGINDRAG通知を親ウィンドウに送信する。この時に、ドラッグ中に表示されるドラッグイメージを作成する。
ツリービューコントロールにTVM_CREATEDRAGIMAGEを送信して、現在ドラッグ中のアイテムに用いられているイメージからドラッグイメージを作成しましたよ、という通知を行う。
ツリービューコントロールは1つのドラッグイメージに対してイメージリストを作成し、そのイメージリストへのハンドルを返す。- ドラッグイメージを作成し終えたら、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にすればよい。- ドラッグイメージを表示する準備が整えば、ImageList_DragEnter関数をCALLして、ウィンドウ内にドラッグイメージを表示させる。
ImageList_DragEnter PROTO hwndLock:DWORD, x:DWORD, y:DWORD
- hwndLockはドラッグイメージを表示するウィンドウへのハンドルで、ドラッグイメージはウィンドウ外に移動できない。
- x と yはドラッグイメージが初期状態で表示されている場所のX、Y座標を表す。これはウィンドウの左上が原点となっており、クライアントエリアが基準でないので注意すること
- そうすると、ドラッグイメージがウィンドウに表示されるようになるが、ツリービューコントロール内のドラッグ操作のサポートをしなければならない。でも、それほど難しいことではない。WM_MOUSEMOVEメッセージでドラッグされる経路とWM_LBUTTONUPメッセージでドロップされる座標値を監視しなければならない。
これはSetCapture関数によりマウス入力をキャプチャーすることで、監視できるようになる。この関数をCALLすれば、マウスカーソルがどこにあろうと、指定されたウィンドウに直接、マウスメッセージがとんでくるようになる。- WM_MOUSEMOVEを扱うところで、ImageList_DragMove関数をCALLしてドラッグ経路を更新していく。この関数により、ドラッグアンドドロップ操作中にドラッグされているイメージを移動できる。
さらに、ドラッグイメージがアイテムと重なった時には、それをチェックするためのTVM_HITTESTを送信することにより、そのアイテムをハイライトさせることができる。
ただし、TVM_SELECTITEMメッセージを送信する前に、先にドラッグイメージを隠しておかないと、ドラッグイメージの跡が汚く残ってしまうので、注意すること。
なので、ImageList_DragShowNolock関数をCALLしてドラッグイメージを隠し、ハイライト処理が終われば、ImageList_DragShowNolock関数をCALLして、再度ドラッグイメージを表示できるようになる。- ユーザがドラッグ中に左マウスボタンを離すと、やらないといけないことがいくつか発生する。もしアイテムをハイライトさせていれば、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
|
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して、ドラッグモードも終了する。 ツリービューアイテムをカッコよく見せるには、 最後にハイライトしたアイテムをチェックして、選択し、 ハイライトを解除しなければならない。 そうしないと、他のアイテムが選択されたときにハイライトしなくなってしまう。 そして最後に、マウスのキャプチャを終了する。