Tutorial 19: Tree View
Control
In this
tutorial, we will learn how to use tree view control. Moreover, we will
also learn how to do drag and drop under tree view control and how to use
an image list with it.
Download
the example here.
Theory:
A tree
view control is a special kind of window that represents objects in hierarchical
order. An example of it is the left pane of Windows Explorer. You can use
this control to show relationships between objects.
You
can create a tree view control by calling CreateWindowEx, passing "SysTreeView32"
as the class name or you can incorporate it into a dialog box. Don't forget
to put InitCommonControls call in your code.
There
are several styles specific to the tree view control. These three are the
ones mostly used.
TVS_HASBUTTONS
== Displays plus (+) and minus (-) buttons next to parent items. The user
clicks the buttons to expand or collapse a parent item's list of child
items. To include buttons with items at the root of the tree view, TVS_LINESATROOT
must also be specified.
TVS_HASLINES
== Uses lines to show the hierarchy of items.
TVS_LINESATROOT
== Uses lines to link items at the root of the tree-view control. This
value is ignored if TVS_HASLINES is not also specified.
The tree
view control, like other common controls, communicates with the parent
window via messages. The parent window can send various messages to it
and the tree view control can send "notification" messages to its parent
window. In this regard, the tree view control is not different that any
window.
When
something interesting occurs to it, it sends a WM_NOTIFY
message
to the parent window with accompanying information.
WM_NOTIFY
wParam
== Control ID, this value is not guaranteed to be unique so we don't use
it.
Instead, we use hwndFrom or IDFrom member of the NMHDR structure
pointed to by lParam
lParam
== Pointer to NMHDR structure. Some controls may pass a pointer to larger
structure but it must have a NMHDR structure as its first member.
That is, when you have lParam, you can be sure that it points to a
NMHDR structure at least.
Next we
will examine NMHDR structure.
NMHDR
struct DWORD
hwndFrom DWORD ?
idFrom DWORD ?
code
DWORD ?
NMHDR
ends
hwndFrom
is the window handle of the control that sends this WM_NOTIFY message.
idFrom
is the control ID of the control that sends this WM_NOTIFY message.
code
is the actual message the control wants to send to the parent window.
Tree
view notifications are those with TVN_ at the beginning of the name. Tree
view messages are those with TVM_, like
TVM_CREATEDRAGIMAGE. The
tree view control sends TVN_xxxx in the code member of NMHDR. The parent
window can send TVM_xxxx to control it.
Adding
items to a tree view control
After
you create a tree view control, you can add items to it. You can do this
by sending TVM_INSERTITEM
to it.
TVM_INSERTITEM
wParam
= 0;
lParam
= pointer to a TV_INSERTSTRUCT;
You should
know some terminology at this point about the relationship between items
in the tree view control.
An
item can be parent, child, or both at the same time. A parent item is the
item that has some other subitem(s) associated with it. At the same time,
the parent item may be a child of some other item. An item without a parent
is called a root item. There can be many root items in a tree view control.
Now we examine TV_INSERTSTRUCT structure
TV_INSERTSTRUCT
STRUCT DWORD
hParent DWORD
?
hInsertAfter DWORD ?
ITEMTYPE <>
TV_INSERTSTRUCT
ENDS
hParent
= Handle to the parent item. If this member is the TVI_ROOT value
or NULL, the item is inserted at the root of the tree-view control.
hInsertAfter
= Handle to the item after which the new item is to be inserted or one
of the following values:
-
TVI_FIRST
==> Inserts the item at the beginning of the list.
-
TVI_LAST
==> Inserts the item at the end of the list.
-
TVI_SORT
==> Inserts the item into the list in alphabetical order.
ITEMTYPE
UNION
itemex TVITEMEX <>
item TVITEM <>
ITEMTYPE
ENDS
We will
use only TVITEM here.
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
This structure
is used to send and receive info about a tree view item, depending on messages.
For example, with TVM_INSERTITEM, it is used to specify the attribute
of the item to be inserted into the tree view control. With TVM_GETITEM,
it'll be filled with information about the selected tree view item.
imask
is
used to specify which member(s) of the TV_ITEM structure is (are)
valid. For example, if the value in imask is TVIF_TEXT, it means
only the pszText member is valid. You can combine several flags together.
hItem
is
the handle to the tree view item. Each item has its own handle, like a
window handle. If you want to do something with an item, you must select
it by its handle.
pszText
is the pointer to a null-terminated string that is the label of the tree
view item.
cchTextMax
is used only when you want to retrieve the label of the tree view item.
Because you will supply the pointer to the buffer in pszText, Windows has
to know the size of the provided buffer. You have to give the size of the
buffer in this member.
iImage
and
iSelectedImage
refers to the index into an image list that contains the images to be shown
when the item is not selected and when it's selected. If you recall Windows
Explorer left pane, the folder images are specified by these two members.
In
order to insert an item into the tree view control, you must at least fill
in the hParent, hInsertAfter and you should fill imask and pszText members
as well.
Adding
images to the tree view control
If you
want to put an image to the left of the tree view item's label, you have
to create an image list and associate it with the tree view control. You
can create an image list by calling ImageList_Create.
ImageList_Create
PROTO cx:DWORD, cy:DWORD, flags:DWORD, \
cInitial:DWORD, cGrow:DWORD
This function
returns the handle to an empty image list if successful.
cx
==
width of each image in this image list, in pixels.
cy
== height of each image in this image list, in pixels. Every image in an
image list must be equal to each other in size. If you specify a large
bitmap, Windows will use cx and cy to *cut* it into several pieces. So
you should prepare your own image as a strip of pictures with identical
dimensions.
flags
== specify the type of images in this image list whether they are color
or monochrome and their color depth. Consult your win32 api reference for
more detail
cInitial
== The number of images that this image list will initially contain. Windows
will use this info to allocate memory for the images.
cGrow
== Amount of images by which the image list can grow when the system needs
to resize the list to make room for new images. This parameter represents
the number of new images that the resized image list can contain.
An
image list is not a window! It's only an image deposit for use by other
windows.
After
an image list is created, you can add images to it by calling ImageList_Add
ImageList_Add
PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD
This function
returns -1 if unsuccessful.
himl
==
the handle of the image list you want to add images to. It is the value
returned by a successful call to ImageList_Create
hbmImage
== the handle to the bitmap to be added to the image list. You usually
store the bitmap in the resource and load it with LoadBitmap call.
Note that you don't have to specify the number of images contained in this
bitmap because this information is inferred from cx and cy parameters passed
to ImageList_Create call.
hbmMask
== Handle to the bitmap that contains the mask. If no mask is used with
the image list, this parameter is ignored.
Normally,
we will add only two images to the image list for use with the tree view
control: one that is used when the tree view item is not selected, and
the other when the item is selected.
When
the image list is ready, you associate it with the tree view control by
sending TVM_SETIMAGELIST
to the tree view control.
TVM_SETIMAGELIST
wParam
= type of image list to set. There are two choices:
-
TVSIL_NORMAL
Set the normal image list, which contains the selected and unselected images
for the tree-view item.
-
TVSIL_STATE
Set the state image list, which contains the images for tree-view items
that are in a user-defined state.
lParam
= Handle to the image list
Retrieve
the info about tree view item
You can
retrieve the information about a tree view item by sending TVM_GETITEM
message.
TVM_GETITEM
wParam
= 0
lParam
= pointer to the TV_ITEM structure to be filled with the information
Before
you send this message, you must fill imask member with the flag(s) that
specifies which member(s) of TV_ITEM you want Windows to fill. And
most importantly, you must fill hItem with the handle to the item you want
to get information from. And this poses a problem: How can you know the
handle of the item you want to retrieve info from? Will you have to store
all tree view handles?
The
answer is quite simple: you don't have to. You can send TVM_GETNEXTITEM
message to the tree view control to retrieve the handle to the tree view
item that has the attribute(s) you specified. For example, you can query
the handle of the first child item, the root item, the selected item, and
so on.
TVM_GETNEXTITEM
wParam
= flag
lParam
= handle to a tree view item (only necessary for some flag values)
The value
in wParam is very important so I present all the flags below:
-
TVGN_CARET
Retrieves the currently selected item.
-
TVGN_CHILD
Retrieves the first child item of the item specified by the hitem parameter
-
TVGN_DROPHILITE
Retrieves the item that is the target of a drag-and-drop operation.
-
TVGN_FIRSTVISIBLE
Retrieves the first visible item.
-
TVGN_NEXT
Retrieves
the next sibling item.
-
TVGN_NEXTVISIBLE
Retrieves the next visible item that follows the specified item. The specified
item must be visible. Use the TVM_GETITEMRECT message to determine whether
an item is visible.
-
TVGN_PARENT
Retrieves the parent of the specified item.
-
TVGN_PREVIOUS
Retrieves the previous sibling item.
-
TVGN_PREVIOUSVISIBLE
Retrieves the first visible item that precedes the specified item. The
specified item must be visible. Use the TVM_GETITEMRECT message to determine
whether an item is visible.
-
TVGN_ROOT
Retrieves the topmost or very first item of the tree-view control.
You can
see that, you can retrieve the handle to the tree view item you are interested
in from this message. SendMessage returns the handle to the tree
view item if successful. You can then fill the returned handle into hItem
member of TV_ITEM
to be used with TVM_GETITEM message.
Drag and
Drop Operation in tree view control
This part
is the reason I decided to write this tutorial. When I tried to follow
the example in win32 api reference (the win32.hlp from InPrise), I was
very frustrated because the vital information is lacking. From trial and
error, I finally figured out how to implement drag & drop in a tree
view control and I don't want anyone to walk the same path as myself.
Below
is the steps in implementing drag & drop operation in a tree view control.
-
When the
user tries to drag an item, the tree view control sends TVN_BEGINDRAG
notification to the parent window. You can use this opportunity to create
a drag image which is the image that will be used to represent the item
while it's being dragged. You can send TVM_CREATEDRAGIMAGE to the
tree view control to tell it to create a default drag image from the image
that is currently used by the item that will be dragged. The tree view
control will create an image list with just one drag image and return the
handle to that image list to you.
-
After
the drag image is created, you specify the hotspot of the drag image by
calling ImageList_BeginDrag.
ImageList_BeginDrag
PROTO himlTrack:DWORD, \
iTrack:DWORD , \
dxHotspot:DWORD, \
dyHotspot:DWORD
himlTrack
is the handle to the image list that contains the drag image.
iTrack
is the index into the image list that specifies the drag image
dxHotspot
specifies the relative distance of the hotspot in horizontal plance in
the drag image since this image will be used in place of the mouse cursor,
so we need to specify which part of the image is the hotspot.
dyHotspot
specifies the relative distance of the hotspot in the vertical plane.
Normally,
iTrack would be 0 if you tell the tree view control to create the drag
image for you. and dxHotspot and dyHotspot can be 0 if you want the left
upper corner of the drag image to be the hotspot.
-
When the
drag image is ready to be displayed, we call ImageList_DragEnter
to display the drag image in the window.
ImageList_DragEnter
PROTO hwndLock:DWORD, x:DWORD, y:DWORD
hwndLock
is the handle of the window that owns the drag image. The drag image will
not be able to move outside that window.
x
and y are the x-and y-coordinate of the place where the drag image
should be initially displayed. Note that these values are relative to the
left upper corner of the window, not the client area.
-
Now that
the drag image is displayed on the window, you will have to support the
drag operation in the tree view control. However, there is a little problem
here. We have to monitor the drag path with WM_MOUSEMOVE and the
drop location with WM_LBUTTONUP messages. However, if the drag image
is over some other child windows, the parent window will never receive
any mouse message. The solution is to capture the mouse input with SetCapture.
Using the call, the mouse messages will be directed to the specified window
regardless of where the mouse cursor is.
-
Within
WM_MOUSEMOVE
handler, you will update the drag path with ImageList_DragMove call. This
function moves the image that is being dragged during a drag-and-drop operation.
Furthermore, if you so desire, you can hilite the item that the drag image
is over by sending TVM_HITTEST to check if the drag image is over
some item. If it is, you can send TVM_SELECTITEM with
TVGN_DROPHILITE
flag to hilite that item. Note that before sending
TVM_SELECTITEM
message,
you must hide the drag image first else your drag image will leave ugly
traces. You can hide the drag image by calling ImageList_DragShowNolock
and, after the hilite operation is finished, call ImageList_DragShowNolock
again to show the drag image.
-
When the
user releases the left mouse button, you must do several things. If you
hilite an item, you must un-hilite it by sending TVM_SELECTITEM
with TVGN_DROPHILITE flag again, but this time, lParam MUST be zero.
If you don't un-hilite the item, you will get a strange effect: when you
select some other item, that item will be enclosed by a rectangle but the
hilite will still be on the last hilited item. Next, you must call ImageList_DragLeave
followed by ImageList_EndDrag. You must release the mouse by calling
ReleaseCapture.
If you create an image list, you must destroy it by calling ImageList_Destroy.
After that, you can go on with what your program wants to do when the drag
& drop operation is completed.
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:
Within
WM_CREATE handler, you create the tree view control
invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\
WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+TVS_LINESATROOT,0,\
0,200,400,hWnd,NULL,\
hInstance,NULL
Note the
styles. TVS_xxxx are the tree view specific styles.
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
Next,
you create an empty image list with will accept images of 16x16 pixels
in size, 16-bit color and initially, it will contain 2 images but can be
expanded to 10 if need arises. We then load the bitmap from the resource
and add it to the image list just created. After that, we delete the handle
to the bitmap since it will not be used anymore. When the image list is
all set, we associate it with the tree view control by sending TVM_SETIMAGELIST
to the tree view control.
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
We insert
items into the tree view control, beginning from the root item. Since it
will be root item, hParent member is NULL and hInsertAfter is TVI_ROOT.
imask member specifies that pszText, iImage and iSelectedImage members
of the TV_ITEM structure is valid. We fill those three members with appropriate
value. pszText contains the label of the root item, iImage is the index
into the image in the image list that will be displayed to the left of
the unselected item, and iSelectedImage is the index into the image in
the image list that will be displayed when the item is selected. When all
appropriate members are filled in, we send TVM_INSERTITEM message to the
tree view control to add the root item to it.
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
After
the root item is added, we can attach the child items to it. hParent member
is now filled with the handle of the parent item. And we will use identical
images in the image list so we don't change iImage and iSelectedImage member.
.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
Now when
the user tries to drag an item, the tree view control sends WM_NOTIFY message
with TVN_BEGINDRAG as the code. lParam is the pointer to an NM_TREEVIEW
structure which contains several pieces of information we need so we put
its value into edi and use edi as the pointer to NM_TREEVIEW structure.
assume edi:ptr NM_TREEVIEW is a way to tell MASM to treat edi as the pointer
to NM_TREEVIEW structure. We then create a drag image by sending TVM_CREATEDRAGIMAGE
to the tree view control. It returns the handle to the newly created image
list with a drag image inside. We call ImageList_BeginDrag to set the hotspot
in the drag image. Then we enter the drag operation by calling ImageList_DragEnter.
This function displays the drag image at the specified location in the
specified window. We use ptDrag structure that is a member of NM_TREEVIEW
structure as the point where the drag image should be initially displayed.After
that, we capture the mouse input and set the flag to indicate that we now
enter drag mode.
.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
Now we
concentrate on WM_MOUSEMOVE. When the user drags the drag image along,
our parent window receives WM_MOUSEMOVE messages. In response to these
messages, we update the drag image position with ImageList_DragMove. After
that, we check if the drag image is over some item. We do that by sending
TVM_HITTEST message to the tree view control with a point for it to check.
If the drag image is over some item, we hilite that item by sending TVM_SELECTITEM
message with TVGN_DROPHILITE flag to the tree view control. During the
hilite operation, we hide the drag image so that it will not leave unsightly
blots on the tree view control.
.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
When the
user releases the left mouse button, the drag operation is at the end.
We leave the drag mode by calling ImageList_DragLeave, followed by ImageList_EndDrag
and ImageList_Destroy. To make the tree view items look good, we also check
the last hilited item, and select it. We must also un-hilite it else the
other items will not get hilited when they are selected. And lastly, we
release the mouse capture.
[Iczelion's
Win32 Assembly Homepage]