Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1801435
  • 博文数量: 290
  • 博客积分: 10653
  • 博客等级: 上将
  • 技术积分: 3178
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-24 23:08
文章存档

2013年(6)

2012年(15)

2011年(25)

2010年(86)

2009年(52)

2008年(66)

2007年(40)

分类:

2008-01-10 00:58:12

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.

一个树视图控件是一种很特别的窗口,它表征了多个对象之间的层次关系。它的一个例子就是windows资源管理器左边的长方块。你能用这个控件显示对象之间的联系。你可以通过调用CreateWindowEx函数创建一个树视图,传递“SysTreeView32”字串作为类名或者是将它并入到一个对话框中。不要忘记在你的代码中放置InitCommonControls函数调用。

树视图有多种样式供用户选择使用。但这三种是用户通常使用的。

 

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_HASBUTTONS=紧接于父选项显示加号(+)和(-)减号按钮。用户点击按钮来展开和折叠一个父选项的子项目列表。选项包含这两个按钮是树视图的根本,所以TVS_LINESATROOT必须被指定。


TVS_HASLINES == Uses lines to show the hierarchy of items.

TVS_HASLINES =   用线条来显示选项之间的层次关系
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.

TVS_LINESATROOT= 用线来连接选项是树视图的根本。如果TVS_HASLINES没有被指定,这个值就是看忽略的。

 

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.

树视图和其它的通用控件一样,也是通过消息和父窗口进行通讯。父窗口可以发送不同的消息给它并且树视图能也能发送“NOTIFICATION“消息给它的父窗口。在这点上,树视图和其它任何窗口并没有什么不同。
When something interesting occurs to it, it sends a WM_NOTIFY message to the parent window with accompanying information.

当一些它感兴趣的事件发生时,它就发送一个WM_NOTIFY消息给它的父窗口并伴随着附加信息。

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

wParam == 控件ID,这个值不能保证它的唯一性,所以我们不用它。
作为替代,我们使用由lParam参数指向的NMHDR结构的hwndFromIdfrom成员。
                   
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.

lParam == 指向NMHDR的指针。一些控件可能传递一个指向一个大结构的指针,但是它必须要有一个NMHDR结构作为它的第一个成员。也就是,当你有一个lParam时,你必须确信它至少指向一个NMHDR结构体。

 

Next we will examine NMHDR structure.

下面,我们分析这个NMHDR结构体。

 

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.

HwndFrom是发送WM_NOTLFY消息的控件窗口句柄,。


idFrom is the control ID of the control that sends this WM_NOTIFY message.

idFrom是发送WM_NOTIFY消息的控件ID值。


code is the actual message the control wants to send to the parent window.

Code实际是控件想发送给父窗口的消息。


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.

树视图的通知名前面有TVN_这样的前缀。树视图的消息前面的前缀是TVM_,如:

TVM_CREATEDRAGIMAGE。树视图控件发送TVN_XXXX这样的消息在NMHDRcode成员中。而父窗口发送TVM_XXXX这样的消息来控制它。

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给它时做到这一点。

 

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

 

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.

hParent = 父选项的句柄。如果这个成员的值为TVI_ROOT或是NULL,那么这个选项作为树视图的根项。
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_FIRST ==》在列表的开始插入新项
  • TVI_LAST ==> Inserts the item at the end of the list.
  • TVI_LAST ==》在列表的最后插入新项
  • TVI_SORT ==> Inserts the item into the list in alphabetical order.
  • TVI_SORT è 按字母顺序插入新项。

ITEMTYPE UNION
        itemex TVITEMEX <>
        item TVITEM <>
ITEMTYPE ENDS

 

We will use only TVITEM here.

我们将这里仅使用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

 

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.

这个结构依赖于消息,用于发送和接收一个树视图选项的信息。例如:有TVM_INSERTITEM,它被用于指定插入到树视图控件中的选项的属性。而对于TVM_GETITEM,当树视图中的选项被选中时,它将填充这个选项的信息。

 

 


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.

Imask被用于指定TV_ITEM结构中的那个成员是有效的。例如,如果在imask中的值是TVIF_TEXT,它意味着只有psztext成员是有效的。你能将几个标记组合在一起。


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.

hItem 是一个指向树视图项的句柄。每一个项目都有它自己的句柄,就像窗口一样。如果你想让一个项目做一些事,你必须通过它的句柄选择它。


pszText is the pointer to a null-terminated string that is the label of the tree view item.

PszText是一个指向以空字符结束的字串,这个字符串是树视图项的标签。


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.

CchTextMax仅当你想得到树视图项的标签时才用这个参数。因为你在pszText中为指针提供了缓冲区,windows必须知道这个被提供的缓冲区的大小。所以,你必须在成员中给定缓冲区的大小。


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.

iImageiSelectedImage引用一个图像列表的索引号,当一个项被选定或没被选定时,图像列表将对应的图像显示出来。如果你回想一下windows资源管理器左边的长方块,文件夹的图标就是被这两个成员指定。


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.

为了插入一个选项到树视图控件中,你至少要填写hParenthInsertAfter成员,另外您还要设定imaskpszText值。  

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函数创建一个图像列表。

 

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.

Cx ==在图像列表中的每一个图像的宽,以像素为单位。
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.

Cy ==在图像列表中的每一个图像的高,以像素为单位。在图像列表中的每一个图像彼此之间的大小必须相等。如果你指定了一张大的位图,windows会按cxcy的标准来把这张图剪切成几块。所以你自己准备的图像应该有相同大小的尺寸。
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

Flags == 指定在图像列表中的图像类型。诸如它们是否有颜色或者是不是单色和它们的色深。更多的细节请参考你的win32 api 手册。


cInitial == The number of images that this image list will initially contain. Windows will use this info to allocate memory for the images.

cInitial == 图像列表中最初包含的图像数目。Windows将用这信息为这些图像分配内存。


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.

cGrow == 当系统需要调整图像列表的大小以便为新增的图像腾出空位时,cGrow参数表示这个图像列表还能增加的图像数目。一个图像列表并不是一个窗口,它仅仅是存放其它窗口使用到的图像。


After an image list is created, you can add images to it by calling ImageList_Add

在图像列表被创建后,你能通过调用ImageList_Add函数来为它添加图像。

 

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

 

This function returns -1 if unsuccessful.

如果函数调用不成功,返回值为-1.
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

Himl == 你想添加到图像列表中的图像的句柄。它是调用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.

HbmImage == 将被添加到图像列表中的位图的句柄。你通常在资源中储存这个位图并通过调用LoadBitmap函数来装载它。注意:你没有必要指定这个位图所包含的图像的数目,因为这些信息能从传递给ImageList_Create函数的cxcy参数中被推定。
hbmMask == Handle to the bitmap that contains the mask. If no mask is used with the image list, this parameter is ignored.

HbmMask == 包含者掩码的位图句柄。如果掩码不被用于图像列表中,这个参数被忽略。


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消息给树视图控件将图像列表和树视图联结在一起。

 

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_NORMAL 设置正常的图像列表,对于树视图项目,它包含被选定和未被选定两种图像。

    • TVSIL_STATE Set the state image list, which contains the images for tree-view items that are in a user-defined state.

TVSIL_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消息来获取有关一个树视图选项的信息。

 

TVM_GETITEM
wParam = 0
lParam = pointer to the TV_ITEM structure to be filled with the information  
指向一个被填充的TV_ITEM结构体的指针。

       

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?

 

在你发送这个消息之前,你必须用Flag的值填充imask成员。这个flag指定那一个TV_ITEM的成员是你想要windows填充的。更重要的是,你必须把你想得到信息的那个选项的句柄传给hItem成员。然而这样产生了一个问题:你如何得到你想检索信息的那个项目的句柄?是不是要储存所有的树视图句柄?

 


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消息给树视图控件来获取那个有你指定的属性值的树视图选项。例如:你能查询第一个孩子选项,根选项,已经被选定的选项的句柄,等等。

TVM_GETNEXTITEM
wParam = flag
lParam = handle to a tree view item (only necessary for some flag values)
一个树视图选项的句柄(仅对于一些flag值才是必须的)

 

The value in wParam is very important so I present all the flags below:

wParam中的值是非常重要的,所以我把所有的标记呈现在下面:

    • TVGN_CARET Retrieves the currently selected item.

TVGN_CARET 检索当前选中的项。

    • TVGN_CHILD Retrieves the first child item of the item specified by the hitem parameter

TVGN_CHILD 检索由hitem参数指定的选项的第一个孩子选项。

    • TVGN_DROPHILITE Retrieves the item that is the target of a drag-and-drop operation.

TVGND_DROPHILITE 检索那个拖放操作的目标项。

    • TVGN_FIRSTVISIBLE Retrieves the first visible item.

TVGN_FIRSTVISIBLE 检索第一个可见的项。

    • TVGN_NEXT Retrieves the next sibling item.

TVGN_NEXT 检索下一个兄弟项。

    • 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_NEXTVISIBLE 检索跟指定项后下一个显示的项。这个指定的项必须可见的。用TVM_GETITEMRECT消息来判断一个项是不是可见的。

    • TVGN_PARENT Retrieves the parent of the specified item.

TVGN_PARENT 检索指定项的父亲。

    • TVGN_PREVIOUS Retrieves the previous sibling item.

TVGN_PREVIOUS 检索上一个兄弟项

    • 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_PREVIOUSVISILE 检索先前指定的第一个可见项。这个指定项必须是可见的。用TVM_GETITEMRECT消息来确定一个项目是否是可见的。

    • GN_ROOT Retrieves the topmost or very first item of the tree-view control.

GN_ROOT 检索树视图的最高项或者是真正的第一项。

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.

 

如你所见,你能从消息中得到你感兴趣的树视图选项句柄。如果成功,SendMessage函数返回这个树视图选项的句柄。你能把这个句柄传递给TV_ITEM结构体的成员hIetm,以便在TVM_GETITEM消息中使用它。

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.

这一部分是我决定写这一课的原因。当我尝试跟随在win32 api手册中的例子时(从InPrise获得win32.Hlp)我是非常失败的,因为至关重要的信息是缺乏的。

从试验和错误中,我最后终于解决了如何在树视图控件中进行拖放操作这一难题。我也不希望任何人再步我后尘。下面是在树视图中进行拖放操作的步骤:

  1. 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.

当用户尝试拖拉一个项目时,树视图发送TVN_BEGINDRAG消息通知它的父窗口。你能利用这个时机来创建一个拖拉的图像,当这个项目被拖拉时,这个图像将被用于代表这个项。你也可以发送TVM_CREATEDRAGIMAGE消息给树视图控件,用来告诉它,从那个被拖动的 选项 当前使用的 图像 中创建一个自定义的拖动图像。这个树视图将创建一个仅有一个拖动图像的图像列表,并将这个图像列表的句柄返回给你。

  1. After the drag image is created, you specify the hotspot of the drag image by calling ImageList_BeginDrag.

在拖动图像被创建后,你通过调用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.

HimlTrack是包含那个拖拉图像的图像列表句柄。


iTrack is the index into the image list that specifies the drag image

iTrack 是在图像列表中指定的那个拖拉图像的索引值。


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.

DxHotspot 指定热点区域在水平面上的相对距离,因为这个图像将被用于鼠标光标的地方,所以我们指定这张图片的一部分作为热点区域。


dyHotspot specifies the relative distance of the hotspot in the vertical plane.

DyHotspot 指定热点在垂直面上的相对距离。


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.

通常,如果你通知树视图为你创建拖动图像,那么iTrick的值为0.此外,如果你希望热点是拖动图像的左上角,那么dxHotspotdyHotspot的值为0.

  1. When the drag image is ready to be displayed, we call ImageList_DragEnter to display the drag image in the window.

当拖拉图像准备好被显示时,我们调用Imagelist_DragEnter来将图像显示在窗口中。

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.

HwndLock 是拥有拖拉图像的窗口句柄。拖拉图像不能被移到窗口外。


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.

X y 是拖拉图像最初被显示的位置的x坐标和y坐标。这些值相对于窗口的左上角而不是客户区。

  1. 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.

现在这个拖动图像被显示在窗口上,你将在树视图控件上进行拖拉操作。然而,这里有一个小小的问题。我们必须用WM_MOUSEMOVE消息来监视拖动的路径并且用WM_LBUTTONUP消息来监视图像落下的位置。可是,如果拖动图像时越过了某些其它子窗口,那么它的父窗口将接收不到任何鼠标消息。解决的办法是用SetCapture函数捕获鼠标输入。在使用这个函数调用时,鼠标消息被直接指定给窗口而不管鼠标光标在什么地方。

  1. 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.

WM_MOUSEMOVE消息处理程序中,调用ImageList_DragMove函数来更新拖拉路径。在图像被拖放的区间,这个函数移动这个图像。如果你非常希望你在拖动图像的时候那些它经过的项目呈高亮显示,你可以发送TVM_HITTEST消息来检查拖拉图像是否经过某些项。如果经过,你可以发送TVM_SELECTITEM消息并设置TVGN_DROPHILITE标志来让那些项目高亮显示(很醒目吧)。注意,在发送TVM_SELECTITEM之前,你必须先隐藏这个拖拉图像否则这个图像将留下非常丑陋的轨迹。你能通过调用ImageList_DragShowNolock函数来隐藏这个图像,然后在高亮操作完成后,在调用一次ImageList_DragShowNolock函数来显示这个图像。

 

  1. 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.

当用户释放左键时,你必须做几件事情,如果你高亮显示了某个选项,你必须在发送一次TVM_SELECTITEM消息并设置TVGN_DROPHILITE标志来使该项恢复正常,而且在这时,lParam参数必须为0。如果你不恢复高亮显示的项,你将得到一个奇怪的结果:当你选择其它项时,那个项将被一个长方形包住,而高亮显示的项目却一直都很醒目。接下来,你必须在函数ImageLIst_EndDrag调用之后接着调用ImageList_DragLeave函数。你必须调用ReleaseCapture函数来释放鼠标。如果你创建了一个图像列表,你必须调用ImageList_Destroy销毁它。当拖放操作完成后,你能让你程序继续任何其它你想做的事情。

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

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

Note the styles. TVS_xxxx are the tree view specific styles.

注意样式,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

 

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.

接下来,你创建一个空的图像列表,并且初始化接收两张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

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.

我们插入项目到树视图控件中,从根项目开始。因为它是根选项,所以hParant成员是NULL并且hInsertAfterTVI_ROOTImask成员指定那个pszTextTV_ITEM结构体的iImage IselectedIma成员是有效的。我们用适当的值填充这三个成员,pszText成员包含根选项的标签,iImage成员是图像列表中图像的索引号,这个图像将显示在未选定的项目名称的左边,而IselectedImage是选项被选定时,在图像列表中被显示的图像的索引号。当所有合适的成员都填充好时,我们发送TVM_INSERTTEM消息给树视图控件来为它添加一个根选项。

 

        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.

当根选项被添加后,我们能为它加上子选项。hParent成员现在用父选项的句柄填充。(hInsertAfter的值是TVI_LAST并且我们将使用图像列表中的同一图像,所以我们并不改变iImageiSelectedImage成员。

.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.

 

现在当用户尝试拖动一个项目时,树视图发送一个WM_NOTIFY消息并伴随着TVN_BEGINDRAG作为通知码。lParam参数是一个包含几块我们需要的信息的NM_TREEVIEW结构体的指针,所以我们把它们的值放在edi寄存器中并且用edi寄存器作为NM_TREEVIEW结构体的指针。assume edi:ptr NM_TREEVIEW 是让MASMedi作为NM_TREEVIEW结构体的指针的一种方式。然后,我们发送TVW_CREATERAGIMAGE消息给树视图控件来创建一个拖动图像。它的返回值是包含拖拉图像的那个新创建的图像列表句柄。我们调用ImageList_BeginDrag函数来设置拖拉图像的热点。然后我们通过调用ImageList_DragEnter函数进入拖拉操作。这个函数将拖拉的图像显示在指定窗口的指定位置上。我们使用ptDrag结构,这个结构是NM_TREEVIEW结构的成员,它指出了 拖拉图像在窗口上最初显示的位置。在这之后,我们捕获鼠标输入并且设置一个标识用来表示我们现在进入拖动形式。

 

   .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.

 

现在,我们将精力集中在WM_MOUSEMOVE消息上。当用户向前拖动拖拉图像时,我们的父窗口接收到WM_MOUSEMOVE消息。在响应这个消息的程序代码中,我们用ImageList_DragMove来更新拖拉图像的位置。在这之后,我们检查拖拉图像是否经过某些选项。我们通过发送TVM_HITTEST消息给树视图来做这个工作。如果拖拉图像时经过某些选项,我们就发送TVM_SELECTITEM并设置TVGH_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

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.

当用户释放鼠标左键时,拖动操作就被终止。我们通过调用ImageList_DragLeave ImageList_EndDragImageList_Destroy函数来离开拖动模式。为了让树视图好看一些,我们最后也检查醒目项(高亮显示的),并且选定它们。然后让它们从高亮显示恢复到正常状态,否则当其它的项被选择时将不能够被高亮显示。在最后,我们释放鼠标捕获。


This article come from Iczelion's asm page,

风向改变翻译于2008-1-9

 

阅读(4728) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~