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的操作,這裡簡單列一些操作.
- 插入,删除和修改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