from:http://blog.csdn.net/kdwangzi/archive/2008/12/02/3422845.aspx
用于树控件消息处理的几个结构
众所周知,Windows程序是消息驱动模式,各种消息由Windows操作系统侦测得到,并由用户创建的窗口所取得。这时,操作系统将消息的具体信息包含在两个典型的消息参数里:LPARAM和WPARAM,它们都是32位无符号整型参数UINT类型。其实,奥妙就在这两个参数当中。它们依据不同的消息,包含不同的参数格式。比如,当用户在窗口中点击鼠标左键时,就会发送WM_LBUTTIONDOWN消息。想想看,点击消息一定要包含点击的鼠标位置,那么自然地,这个消息中的LPARAM中包含了POINT数据类型。其中,低字节是X轴坐标,高字节是Y轴坐标,可以通过LOWORD和HIWORD宏取得:
POINT pt;
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
同时,消息参数WPARAM包含一些标志位flags,用于确定在点击鼠标的同时是否还按下了键盘上的功能键等等。
而有时候,有些消息需要传递一些比较大的结构struct,这时32位的无符号整数UINT肯定是装不下的。那么,就把传递的结构指针(地址)放在消息参数当中。当窗口接收到这种消息时,用户负责把消息参数转换成适当的指针类型即可。比如下面将要说明的NMHDR结构。当控件向其父窗口发送通知消息WM_NOTIFY时,消息参数LPARAM就是一个结构的地址指针。那么使用时,可以这样处理:
NMHDR *pnmhdr = (NMHDR *)lParam;
此外,对于控件来说,当控件上发生什么事件时(产生了相应的消息),那么控件会向其父窗口发送通知消息WM_NOTIFY,其中的lParam是NMHDR结构地址。给出这个结构还要进一步确定究竟是什么通知消息,这时code字段包含具体的消息类型。它可以是通用控件的通知消息,以NM_XXX形式命名,也可以是具体控件的通知消息。对于树控件来说它们以TVN_XXX形式命名。
另外,还要对控件进行控制,这些控制操作是通过向控件发送消息来实现。这些消息以TVM_XXX形式命名。
好了,可以简单地说,树控件上发生了某种事件时,树控件会向其父窗口发送通知消息WM_NOTIFY, 它们是TVN_XXX;当需要控制树控件时,通过SendMessage向它发送通知消息,它们是TVM_XXX。
1.NMHDR结构
typedef struct tagNMHDR {
HWND hwndFrom;
UINT idFrom;
UINT code;
} NMHDR;
该结构包含有关通知消息的信息。
hwndFrom
发送消息的控件窗口句柄。
idFrom
发送消息的控件标识符(ID)。
code
通知码。此成员可以是特殊控件的通知码,例如,TVN_ITEMEXPANDING.。也可以是通用控件的通知码之一。
通用通知码列表如下:
通知码 | 发送原因 |
NM_CLICK | 用户在控件上单击鼠标左键 |
NM_DBLCLK | 用户在控件上双击鼠标左键 |
NM_RCLICK | 用户在控件上单击鼠标右键 |
NM_RDBLCLK | 用户在控件上双击鼠标右键 |
NM_RETURN | 当控件具有输入焦点时,用户按下回车键 |
NM_SETFOCUS | 控件已经具有输入焦点 |
NM_KILLFOCUS | 控件已经失去焦点 |
NM_OUTOFMEMORY | 因为没有足够的内存可用,控件不能完成指定的操作 |
此结构一般作为控件通知消息处理函数的参数,例如上面代码中的通知消息处理函数:
afx_msg void OnTreeDblClk(NMHDR * pNMHDR, LRESULT *pResult);
――――――――――――――――――――――――――――――――――――――
2.NM_TREEVIEW结构
typedef struct _NM_TREEVIEW {
NMHDR hdr;
UINT action;
TV_ITEM itemOld;
TV_ITEM itemNew;
POINT ptDrag; }
NM_TREEVIEW;
typedef NM_TREEVIEW FAR *LPNM_TREEVIEW;
此结构包含树控件消息的信息。
此结构中action成员是特定的通知消息位。它表明是由鼠标动作引起的选择操作的改变,还是由键盘动作引起的。比如,当在树控件的小加号/减号(+/-)上点击鼠标左键时,带有子节点的项就要展开或者合拢。那么究竟是展开还是合拢,就要通过这个action字段来区分。当action == TVE_EXPAND时,就展开;当action == TVE_COLLAPSE时就合拢。 而itemOld和itemNew分别是旧结点的状态和新结点的状态。ptDrag是引起消息发送的事件发生时的鼠标在客户区上的坐标。
在通知消息处理函数中一般使用HMHDR指针,例如:
afx_msg void OnTreeDblClk(NMHDR * pNMHDR, LRESULT *pResult);
而有时,甚至是大多数时候,在消息处理函数需要使用NM_TREEVIEW对象,这时可以将消息处理函数传进来的NMHDR指针强制转换为NM_TREEVIEW指针。例如:
void CComDlg::OnSelchangedTreeview(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_TREEVIEW1);
HTREEITEM hSelected = pNMTreeView->itemNew.hItem;
if (hSelected != NULL) {
char text[31];
TV_ITEM item;
item.mask = TVIF_HANDLE | TVIF_TEXT;
item.hItem = hSelected;
item.pszText = text;
item.cchTextMax = 30;
VERIFY(pTree->GetItem(&item));
SetDlgItemText(IDC_STATIC_TREEVIEW1,
pTree->GetItemText(hSelected));
}
*pResult = 0;
}
――――――――――――――――――――――――――――――――――――
此函数处理了NM_SELCHANGED消息,在树控件中某项的选择状态被改变后,在一个静态文本框中显示改变后新项的项标签文本。其实这段代码也可以用如下代码代替:
CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_TREEVIEW1);
HTREEITEM hSelected = pTree->GetSelectedItem();
if (hSelected != NULL) {
SetDlgItemText(IDC_STATIC_TREEVIEW1,
pTree->GetItemText(hSelected));
}
pResult = 0;
――――――――――――――――――――――――――――――――――――
区别是前一段代码中选择在程序栈中为项标签文本缓冲区分配内存,而后一段代码中选择在程序堆中为项标签文本分配内存。