TreeView控件是最常用的控件之一,最近研究了一下,一开始是一点头绪都没有,不过多研究一下就越来越清晰了.呵呵.这篇文章也算是我自己的拾遗吧.
虽然windows里的TreeView控件不像Java的MVC结构那样让人感觉很清晰,但是它还是提供了一系列的消息来对TreeView本身的数据结构的处理,所以用熟了的话,也不是很麻烦.其实也有很多相关的宏,但是这些宏好像windows汇编无法使用.但是用宏的话就没有意思了,自己去实现一下也是很不错的嘛.呵呵.
说到树型视图控件,要提到的就是TVITEM(TVITEMEX)结构体,这个结构体是一个项目(元素),它是TreeView的重要组成部分.它的定义为:
typedef struct tagTVITEM{
UINT mask;
HTREEITEM hItem;
UINT state;
UINT stateMask;
LPTSTR pszText;
int cchTextMax;
int iImage;
int iSelectedImage;
int cChildren;
LPARAM lParam;
} TVITEM, FAR *LPTVITEM;
创建TreeView可以简单地用createwindowex函数.而对它的操作,主要是对ITEM的操作,这里简单列一些操作.
虽然windows里的TreeView控件不像Java的MVC结构那样让人感觉很清晰,但是它还是提供了一系列的消息来对TreeView本身的数据结构的处理,所以用熟了的话,也不是很麻烦.其实也有很多相关的宏,但是这些宏好像windows汇编无法使用.但是用宏的话就没有意思了,自己去实现一下也是很不错的嘛.呵呵.
说到树型视图控件,要提到的就是TVITEM(TVITEMEX)结构体,这个结构体是一个项目(元素),它是TreeView的重要组成部分.它的定义为:
typedef struct tagTVITEM{
UINT mask;
HTREEITEM hItem;
UINT state;
UINT stateMask;
LPTSTR pszText;
int cchTextMax;
int iImage;
int iSelectedImage;
int cChildren;
LPARAM lParam;
} TVITEM, FAR *LPTVITEM;
创建TreeView可以简单地用createwindowex函数.而对它的操作,主要是对ITEM的操作,这里简单列一些操作.
- 插入,删除和修改ITEM
- 插入一个ITEM,需要填写TVINSERTSTRUCT结构体.这个结构体包含一个Parent句柄,一个插入位置标志和一个TVITEM结构体.我们并不用全部填写.先看TVINSERTSTRUCT结构体,然后再说明
typedef struct tagTVINSERTSTRUCT {
HTREEITEM hParent;
HTREEITEM hInsertAfter;
#if (_WIN32_IE >= 0x0400)
union
{
TVITEMEX itemex;
TVITEM item;
} DUMMYUNIONNAME;
#else
TVITEM item;
#endif
} TVINSERTSTRUCT, FAR *LPTVINSERTSTRUCT;
hParent是指定这个ITEM的父节点,如果它是根节点,可以为TVI_ROOT或NULL.
hInsertAfter是插入到哪个节点之后,是一个ITEM的句柄,可以用TVI_FIRST等常量
TVITEM里指定要加入项目的属性,并不是所有的都要设定,而是看mask和statemask.如果设置mask为TVIF_TEXT,那么说明这个ITEM是纯TEXT型的,我们只要指定pszText和cchTextMax就行了.可以多种mask值或起来作为参数.如果是TVIF_IMAGE的,必须要为这个TreeView设置一个ImageList.
下面是一个插入ITEM的片段:local u:USER
push hTreeWindow
pop hTreeWin
invoke SendMessage,hTreeWindow,TVM_SETITEMHEIGHT,26,0
invoke ImageList_Create,24,24,ILC_COLOR24,5,2
mov hImageList,eax
invoke LoadBitmap,hInstance,IDC_STATEBIT
push eax
invoke ImageList_Add,hImageList,eax,NULL
pop eax
invoke DeleteObject,eax
invoke SendMessage,hTreeWindow,TVM_SETIMAGELIST,NULL,hImageList
invoke RtlZeroMemory,addr insertTNode.item,sizeof TVITEM
mov insertTNode.hParent,NULL
mov insertTNode.hInsertAfter,TVI_ROOT
mov insertTNode.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE+TVIF_STATE
mov insertTNode.item.pszText,offset parentText
mov insertTNode.item.iImage,3
mov insertTNode.item.iSelectedImage,3
mov insertTNode.item.state,TVIS_BOLD
mov insertTNode.item.stateMask,TVIS_OVERLAYMASK
invoke SendMessage,hTreeWindow,TVM_INSERTITEM,0,addr insertTNode
mov hRootNode,eax - 处理NOTIFY消息
- 个人还是觉得重新写一个类继承TreeView,然后自己处理它的NOTIFY消息是最好的了.呵呵,常用的处理消息就是双击(NM_DBLCLK),正展开(TVN_ITEMEXPANDING)等等消息.这里说说NM_DBLCLK消息,收到这个消息时,肯定是在一个ITEM上发生了双击.想想QQ之类的程序,用户列表都是树型的,双击后就会触发事件,其实就是处理了这个消息.那么怎样得到双击的项目呢,这儿就必须通过HITTEST来取得这个ITEM了(TreeView_SelectItem这个宏应该就是用HITTEST实现的),HITTEST就是看当前鼠标是不是在某个ITEM上,上的话就返回这个ITEM的句柄.那么怎样通过句柄得到这个ITEM的其它属性呢?例如这个元素的文字?方法就是再次填充TVITEM的部分元素,然后通过TVM_GETITEM消息来取得它.TVM_GETITEM会读出TVITEM里的hItem,用它来找到指定元素,然后根据mask里指定的要读取的值,填入TVITEM.所以在发送TVM_GETITEM消息前要填充好mask和hItem!!!mask可以是TVIF_TEXT 等等.这里要非常注意!!!如果你要取得Text,你必须指定pszText指向一个缓冲空间(把pszText设为一个缓冲空间的首地址)和 cchTextMax.这是显然的,因为这个结构体里没有可放字符串的地方.下面这个代码处理了NM_DBLCLK消息:
SelfNotify proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
local TVI:TV_ITEM
local TVHINFO:TV_HITTESTINFO
local textBuf[50]:BYTE
mov esi,lParam
assume esi:ptr NMHDR
.if [esi].code==NM_DBLCLK
invoke GetCursorPos,addr TVHINFO.pt
invoke ScreenToClient,hTreeWin,addr TVHINFO.pt
mov TVHINFO.flags,TVHT_ONITEM or TVHT_ONITEMICON
invoke SendMessage,hTreeWin,TVM_HITTEST,0,addr TVHINFO
mov eax,TVHINFO.hItem
.if eax==NULL || eax==hRootNode
jmp @F
.endif
mov TVI.hItem,eax
mov TVI.imask,TVIF_IMAGE or TVIF_SELECTEDIMAGE or TVIF_TEXT
invoke GetStrAddr,addr textBuf
mov TVI.pszText,eax
mov TVI.cchTextMax,50
invoke SendMessage,hTreeWin,TVM_GETITEM,0,addr TVI
invoke MessageBox,NULL,TVI.pszText,addr textBuf,0
.endif
@@:
invoke DefWindowProc,hWin,uMsg,wParam,lParam ;;;//注意这儿是必不可少的哦!!!!!
ret
SelfNotify endp