宏ON_NOTIFY与WM_NOTIFY消息

Windows 3.x下的通知消息

在windows 3.x下,控件将诸如鼠标点击、内容修改、选择事件、背景绘制等通知消息发送给父窗口处理。简单的通知消息以WM_COMMAND消息发送,并在wParam参数中存放通知码(如BN_CLICKED)和控件ID,在lParam参数中存放控件句柄。因为wParam与lParam都被使用了,也就没得办法去传递额外的数据。简单的通知消息也有传递额外数据的需求,举个例子,点击事件BN_CLICKED发生时,没有办法去传递鼠标当前的位置信息。

在windows 3.x下,解决上述不能传递额外数据的方法就是定义更多特殊目的的消息,如WM_CTLCOLOR、WM_VSCROLL、WM_HSCROLL、WM_DRAWITEM、WM_MEASUREITEM、WM_COMPAREITEM、WM_DELETEITEM、WM_CHARTOITEM、WM_VKEYTOITEM等等。这些消息可以反射回给控件自己处理,之后由另外一篇译文来介绍这项技术。

Win32下的通知消息

为了兼容存在于Windows 3.1中的诸多控件,Win32 API继续使用了Windows 3.x下的很多通知消息。Win32也为Windows 3.x下支持的控件增加了许多更精细、更复杂的控件(增强版),这些控件在发送通知时,往往需要传递额外的数据。Win32 API的设计者们觉得原来那种增加形如WM_*的Windows消息来解决问题的方式不可取,故设计了一种新的方案,只用一个WM_NOTIFY,这个消息可以传递大量数据。

WM_NOTIFY消息在wParam存放控件ID,而在lParam参数中存放了一个指向一块数据结构的指针,这块数据结构可以是NMHDR或任何第一个成员为NMHDR的更大块的数据结构,之后使用的时候,根据逻辑对这个指针进行相应的类型强转即可。

在大多数情况下,这个指针指向的是一块比NMHDR更大的数据结构,故你在使用时,通常需要进行类型强转。只有少部分公共通知消息(如以NM_打头的消息)及信息提示控件中的TTN_SHOW、TTN_POP通知消息才真正地使用NMHDR。

NMHDR结构包含句柄、发送此消息的控件ID以及消息码(如TTN_SHOW),NMHDR结构定义如下:

typedef struct tagNMHDR {
    HWND hwndFrom;
    UINT idFrom;
    UINT code;
} NMHDR;

对于TTN_SHOW消息,上述结构的code将会被设置为TTN_SHOW。

大多数通知消息传递一个指向第一个成员为NMHDR的大块数据结构。举个例子,当一个按键在list view控件中按下时,List View控件会发送一个LVN_KEYDOW通知消息,这个通知消息的lParam存放的指针指向的数据结构为LV_KEYDOWN,LV_KEYDOWN结构如下:

typedef struct tagLV_KEYDOWN {
    NMHDR hdr;
    WORD wVKey;
    UINT flags;
} LV_KEYDOWN;

注:因为LV_KEYDOWN的第一个成员是NMHDR,所以你可以将lParam中的指针强转成NMHDR*,也可强转成LV_KEYDOWN*。

Windows新控件的通用通知

Windows新控件存在一些通用通知,这个通知传送的是一个NMHDR结构,如下:

通知码(code)发送来源
NM_CLICK鼠标左健单击
NM_DBLCLK鼠标左健双击
NM_RCLICK鼠标右键单击
NM_RDBLCLK鼠标右键双击
NM_RETURN控件拥有输入焦点时,回国事件
NM_SETFOCUS控件获得输入焦点
NM_KILLFOCUS控件失去输入焦点
NM_OUTOFMEMORY控件内存溢出

MFC框架宏ON_NOTIFY

CWnd::OnNotify函数处理通知消息,它的默认实现是检测消息映射数组,查找处理函数并调用。通常来说,你不需要去重写OnNotify,需要做的是针对各种控件事件增加相应的处理函数,并使用MFC提供的宏在消息映射表中添加处理项。

通过MFC提供的类向导,你可以创建ON_NOTIFY入口(即自动在消息映射表中添加处理项)。

ON_NOTIFY消息映射宏的法语如下:

ON_NOTIFY(wNotifyCode, id, memberFxn)

参数说明如下:
wNotifyCode:消息通知码,如LVN_KEYDOWN.
id:控制ID
memberFxn:处理函数,函数原型如下:

afx_msg void memberFxn(NMHDR* pNotifyStruct, LRESULT* result);

参数说明如下:
pNotifyStruct:指针,指向第一块内存为NMHDR的数据结束
result:返回结果

举例

假设有个ID为IDC_LIST1CListCtrl,你要使用OnKeydonwList1来处理其LVN_KEYDOWN的通知消息,你可以使用类向导或手动在消息映射表中添加如下项:

ON_NOTIFY(LVN_KEYDOWN, IDC_LIST1, OnKeydownList1)

函数实现如下:

void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)
{
    LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;

    // TODO: Add your control notification handler
    //       code here

    *pResult = 0;
}
ON_NOTIFY_RANGE

如果你想为一组控件ID相邻的控件处理同一个WM_NOTIFY消息,你可以使用ON_NOTIFY_RANGE宏来代替ON_NOTIFY。例如,你可能有一组按钮需要对一个通知消息做出一致的响应,就可使用此宏。

类向导没有提供ON_NOTIFY_RANGE宏的自动生成,所以需要自己手动添加。

宏ON_NOTIFY_RANGE原型如下:

ON_NOTIFY_RANGE(wNotifyCode, id, idLast, memberFxn)

参数说明如下:
wNotifyCode:通知码,如LVN_KEYDOWN
id:第一个控件ID
idLast:最后一个控件ID
memberFxn:处理函数,函数原型如下:

afx_msg void memberFxn(UINT id, NMHDR* pNotifyStruct, LRESULT* result);
ON_NOTIFY_EX, ON_NOTIFY_EX_RANGE

如果你想让不止一个地方处理通知消息,那么你可以使用宏ON_NOTIFY_EX或ON_NOTIFY_EX_RANGE来实现,带有EX后缀的宏与之前版本的宏的唯一区别在于memberFxn的返回值是BOOL,当返回TRUE时,表示这个通知消息已经处理完毕了,不用再路由给其它地方处理了;而返回FALSE时,表示可以继续交给其它处理器来处理此通知消息。

类向导没有提供ON_NOTIFY_EX, ON_NOTIFY_EX_RANGE宏的自动生成功能,所以这两个宏也是需要你自己手动添加。

ON_NOTIFY_EX, ON_NOTIFY_EX_RANGE宏的原型如下:

ON_NOTIFY_EX(nCode, id, memberFxn)
ON_NOTIFY_EX_RANGE(wNotifyCode, id, idLast, memberFxn)

对应的memberFxn函数原型如下:

afx_msg BOOL memberFxn(UINT id, NMHDR* pNotifyStruct, LRESULT* result);

参考

英文版原文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值