第十九课 树型视图控件
本课中,我们将学习如何使用树型视图控件。另外还要学习如何在树型视图中完成拖-拉动作,以及如何使用图象列表。 理论:树型视图是一种特别的窗口,我们可以使用它一目了然地表示某种层次关系。譬如象在资源管理器中左边窗口中的就是树型视图。您可以调用CreateWindowEx来创建树型视图,传递一个类名“"SysTreeView32"”,或者您也可以把它放到一个对话框中去。不要忘了在您的代码中加入InitCommonControls函数。TVM_INSERTITEM
wParam = 0;
lParam = 指向结构体TV_INSERTSTRUCT的指针;
您应当知道一些关于树型视图中的项目之间关系的一些术语。 一个项目可能是一个父亲、儿子或两者都是。父项目下含有子项目,而该父项目又有可能是其它项目的子项目。一个没有父项目的项目叫根项目。在树型视图中可能有多个根项目。现在我们来看看TV_INSERTSTRUCT结构体:
TV_INSERTSTRUCT STRUCT DWORD该结构体根据消息类型,用来发送或接收关于一个树型视图的项目的有关信息。譬如:对于消息TVM_INSERTITEM,它用来指定插入树型视图控件的项目的属性。而对于消息TVM_GETITEM,该结构体用来填充关于选定项目的信息。
imask 用来指定TV_ITEM的那些成员变量有效。譬如,如果指定了TVIF_TEXT,这意味着pszText成员变量是有效的。您可以同时指定几个标志位。
hItem 是树型视图项目的句柄。每一个项目都有它自己的句柄,就像窗口一样。如果您想要操作一个项目,就必须选择它的句柄。
pszText 是一个字符串指针。它是项目的标签名。
cchTextMax仅在查询项目的名称时使用。由于在pszText中指定了指针,WINDOWS还要知道该缓冲去的大小。所以您必须给出该值。
iImage 和 iSelectedImage用来指定图象列表以及一个索引号。这样就知道当项目被选中或没被选中时用哪个图象来表示该项目。像资源管理器中左边窗口中的文件夹等小图表就是有这两个参数来决定的。
为了在树型视图中插入一个项目,您必须至少设定hParent, hInsertAfter,另外您还要设定imask和pszText值。
如果创建成功的话,该函数返回一个空的图象列表的句柄。
cx == 以像素为单位的图象的宽度。
cy == 以像素为单位的图象的高度。图象列表中的每一幅的高度都必须相同。否则WINDOWS会对您的图象进行裁剪,如果过大的话就可能裁剪成几小块。所以您必须指定相同大小的图象。
flags == 指定图象列表的图象的颜色深度。详细情况请参考WIN32 API 指南。
cInitial == 指定包含的图象的数目。WINDWOS将依此来分配合适的内存。
cGrow == 在增加新图象是一次增加的数目。
图象列表不是窗口。仅仅是保存在那给其它的窗口使用的一种资源。 在图象列表产生后,您可以调用ImageList_Add来向其中加入图象。
ImageList_Add PROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD 如果该函数调用失败的话,返回-1。TVM_GETITEM
wParam = 0
lParam =指向结构体TV_ITEM的指针。该结构体将用来得到相关的信息。
在发送该消息前必须设置成员变量imask的值,以便WINDOWS能告诉相关的信息。当然,最重要的是,您必须传递您想得到信息的项目的句柄。这就引起了一个问题,您如何得到项目的句柄?要保存所有项目的句柄吗?
答案是很简单的:没有必要。您可以发送消息TVM_GETNEXTITEM到树型视图以检索您想要得到其属性的项目的句柄。譬如:您可以查询第一个子项目的句柄、根目录的句柄、选中的项目的句柄等等。
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
.const
IDB_TREE equ 4006 ; ID of the bitmap resource
.data
ClassName db "TreeViewWinClass",0
AppName db "Tree View Demo",0
TreeViewClass db "SysTreeView32",0
Parent db "Parent Item",0
Child1 db "child1",0
Child2 db "child2",0
DragMode dd FALSE ; a flag to determine if we are in drag mode
.data?
hInstance HINSTANCE ?
hwndTreeView dd ? ; handle of the tree view control
hParent dd ? ; handle of the root tree view item
hImageList dd ? ; handle of the image list used in the tree view control
hDragImageList dd ? ; handle of the image list used to store the drag image
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
invoke ExitProcess,eax
invoke InitCommonControls
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_APPWORKSPACE
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
CW_USEDEFAULT,200,400,NULL,NULL,\
hInst,NULL
mov hwnd,eax
.while TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endw
mov eax,msg.wParam
ret
WinMain endp
WndProc proc uses edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL tvinsert:TV_INSERTSTRUCT
LOCAL hBitmap:DWORD
LOCAL tvhit:TV_HITTESTINFO
.if uMsg==WM_CREATE
invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\
WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+TVS_LINESATROOT,0,\
0,200,400,hWnd,NULL,\
hInstance,NULL ; Create the tree view control
mov hwndTreeView,eax
invoke ImageList_Create,16,16,ILC_COLOR16,2,10 ; create the associated image list
mov hImageList,eax
invoke LoadBitmap,hInstance,IDB_TREE ; load the bitmap from the resource
mov hBitmap,eax
invoke ImageList_Add,hImageList,hBitmap,NULL ; Add the bitmap into the image list
invoke DeleteObject,hBitmap ; always delete the bitmap resource
invoke SendMessage,hwndTreeView,TVM_SETIMAGELIST,0,hImageList
mov tvinsert.hParent,NULL
mov tvinsert.hInsertAfter,TVI_ROOT
mov tvinsert.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE
mov tvinsert.item.pszText,offset Parent
mov tvinsert.item.iImage,0
mov tvinsert.item.iSelectedImage,1
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
mov hParent,eax
mov tvinsert.hParent,eax
mov tvinsert.hInsertAfter,TVI_LAST
mov tvinsert.item.pszText,offset Child1
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
mov tvinsert.item.pszText,offset Child2
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
.elseif uMsg==WM_MOUSEMOVE
.if DragMode==TRUE
mov eax,lParam
and eax,0ffffh
mov ecx,lParam
shr ecx,16
mov tvhit.pt.x,eax
mov tvhit.pt.y,ecx
invoke ImageList_DragMove,eax,ecx
invoke ImageList_DragShowNolock,FALSE
invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit
.if eax!=NULL
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,eax
.endif
invoke ImageList_DragShowNolock,TRUE
.endif
.elseif uMsg==WM_LBUTTONUP
.if DragMode==TRUE
invoke ImageList_DragLeave,hwndTreeView
invoke ImageList_EndDrag
invoke ImageList_Destroy,hDragImageList
invoke SendMessage,hwndTreeView,TVM_GETNEXTITEM,TVGN_DROPHILITE,0
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_CARET,eax
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,0
invoke ReleaseCapture
mov DragMode,FALSE
.endif
.elseif uMsg==WM_NOTIFY
mov edi,lParam
assume edi:ptr NM_TREEVIEW
.if [edi].hdr.code==TVN_BEGINDRAG
invoke SendMessage,hwndTreeView,TVM_CREATEDRAGIMAGE,0,[edi].itemNew.hItem
mov hDragImageList,eax
invoke ImageList_BeginDrag,hDragImageList,0,0,0
invoke ImageList_DragEnter,hwndTreeView,[edi].ptDrag.x,[edi].ptDrag.y
invoke SetCapture,hWnd
mov DragMode,TRUE
.endif
assume edi:nothing
.elseif uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
end start
分析:在处理消息WM_CREATE的代码中,您可以创建树型视图控件。
invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\
WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+TVS_LINESATROOT,0,\
0,200,400,hWnd,NULL,\
hInstance,NULL 注意: TVS_xxxx 是树型视图所特有的风格。
invoke ImageList_Create,16,16,ILC_COLOR16,2,10
mov hImageList,eax
invoke LoadBitmap,hInstance,IDB_TREE
mov hBitmap,eax
invoke ImageList_Add,hImageList,hBitmap,NULL
invoke DeleteObject,hBitmap
invoke SendMessage,hwndTreeView,TVM_SETIMAGELIST,0,hImageList 接下来,您可以创建一个空的图像列表,该图像列表容纳的是以像素为单位16x16大小和16位深度的图像,该图像列表初始包含两幅图像,最大可以容纳10幅。然后我们从资源中加载图像,并把它们放到图像列表中去。随后我们删除掉图像的句柄,因为我们不需要再用到它。设置好图像列表后,我们通过发送消息TVM_SETIMAGELIST把它和树型视图控件联系起来。
mov tvinsert.hParent,NULL
mov tvinsert.hInsertAfter,TVI_ROOT
mov tvinsert.u.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE
mov tvinsert.u.item.pszText,offset Parent
mov tvinsert.u.item.iImage,0
mov tvinsert.u.item.iSelectedImage,1
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert 现在把项目插入到树型视图控件中去,首先我们从根项目开始。因为是根项目,所以成员变量hParent是NULL,hInsertAfter是TVI_ROOT。imask指定TV_ITEM结构体变量中的pszText,iImage和iSelectedImage三个成员变量的值是有效的。我们应该给这三个成员变量赋上正确的值。其中pszText显示项目的名称,iImage是图像列表中图像的索引号,该图像显示在未选中的项目名称的左边,iSelectedImage是选中的项目的图像索引号。设置好了这些值后,我们发送TVM_INSERTITEM消息给树型视图控件来把根项目加入到树型视图控件中去。
mov hParent,eax
mov tvinsert.hParent,eax
mov tvinsert.hInsertAfter,TVI_LAST
mov tvinsert.u.item.pszText,offset Child1
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
mov tvinsert.u.item.pszText,offset Child2
invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert 加入完根项目后,我们再加入子项目。这时的成员变量hParent是其父项目的句柄,hInsertAfter的值是TVI_LAST。至于选中和非选中时用的图像是一样的,所以我们无需更改其它变量的值。
.elseif uMsg==WM_NOTIFY
mov edi,lParam
assume edi:ptr NM_TREEVIEW
.if [edi].hdr.code==TVN_BEGINDRAG
invoke SendMessage,hwndTreeView,TVM_CREATEDRAGIMAGE,0,[edi].itemNew.hItem
mov hDragImageList,eax
invoke ImageList_BeginDrag,hDragImageList,0,0,0
invoke ImageList_DragEnter,hwndTreeView,[edi].ptDrag.x,[edi].ptDrag.y
invoke SetCapture,hWnd
mov DragMode,TRUE
.endif
assume edi:nothing 当用户拖动项目时,树型视图控件将发送WM_NOTIFY消息给它的父窗口,子消息号是TVN_BEGINDRAG。在lPAram中是指向结构体NM_TREEVIEW 的指针,该结构体包含了一些附加信息。我们把lParam的值放到edi寄存器中,这样就可以把edi作为一个指针来使用。“assume edi:ptr NM_TREEVIEW ”语句用来告诉编译器MASM把edi作为指向NM_TREEVIEW 的结构体的变量使用。我们通过发送消息TVM_CREATEDRAGIMAGE来创建一个拖动的图像。它将返回一个新创建的图像列表的句柄,该图像列表中包含拖动中的图像。我们调用ImageList_BeginDrag函数设置拖动图像的热点。调用ImageList_DragEnter函数进入操作。该函数会在特定位置显示拖动中的图像。起初显示的位置我们设在结构体NM_TREEVIEW中的成员变量ptDrag所指的位置。我们锁定鼠标的输入,并设置标志变量,表示我们进入了拖拉操作。
.elseif uMsg==WM_MOUSEMOVE
.if DragMode==TRUE
mov eax,lParam
and eax,0ffffh
mov ecx,lParam
shr ecx,16
mov tvhit.pt.x,eax
mov tvhit.pt.y,ecx
invoke ImageList_DragMove,eax,ecx
invoke ImageList_DragShowNolock,FALSE
invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit
.if eax!=NULL
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,eax
.endif
invoke ImageList_DragShowNolock,TRUE
.endif 现在我们来看看WM_MOUSEMOVE消息的处理过程。当用户拖动图像时,我们的父窗口将接收到WM_MOUSEMOVE。为了响应这些消息,我们调用ImageList_DragMove来更新更新图像的位置。然后我们发送消息TVM_HITTEST给列表视图控件看看拖拉中的图像是否正好经过某些项目的上面,当然还要附带传递坐标位置等信息。如果经过的话,我们发送消息TVM_SELECTITEM并附带TVGN_DROPHILITE标志给树型视图控件,后者将会高亮度显示正被经过的项目。在高亮度显示的过程中,我们隐藏掉拖动中的图像免得显示的图像难看。
.elseif uMsg==WM_LBUTTONUP
.if DragMode==TRUE
invoke ImageList_DragLeave,hwndTreeView
invoke ImageList_EndDrag
invoke ImageList_Destroy,hDragImageList
invoke SendMessage,hwndTreeView,TVM_GETNEXTITEM,TVGN_DROPHILITE,0
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_CARET,eax
invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,0
invoke ReleaseCapture
mov DragMode,FALSE
.endif 当用户释放鼠标左键后,拖拉操作就可以结束了。我们调用ImageList_DragLeave,ImageList_EndDrag和ImageList_Destroy来结束拖拉操作模式。为了使得树形视图控件好看,我们检查最后高亮度显示的项目,并且选中它。我们还必须使得其不高亮度显示,否则其它的项目被选中时就不能高亮度显示了。最后我们释放对鼠标输入事件的捕获。
本文地址:http://com.8s8s.com/it/it30022.htm