源码解释
通知事件的接收指令原型
#define ON_NOTIFY(wNotifyCode, id, memberFxn) \
{ WM_NOTIFY, (WORD)(int)wNotifyCode, (WORD)id, (WORD)id, AfxSigNotify_v, \
(AFX_PMSG) \
(static_cast< void (AFX_MSG_CALL CCmdTarget::*)(NMHDR*, LRESULT*) > \
(memberFxn)) },
wNotifyCode为通知命令
id 为发送这个通知消息的控件ID
memberfxn 则为响应消息
从这里可以看到 传参NMHDR*和LRESULT*是发送消息传给这里的核心数据
typedef struct tagNMHDR
{
HWND hwndFrom;
UINT_PTR idFrom;
UINT code; // NM_ code
} NMHDR;
其中NMHDR 的 hwndFrom 即为窗口句柄来源 一般为发送这个消息的窗口句柄 不可为空 从下面的源码中可以得知 id 就是通过这个句柄获取的
idFrom 是发送这个消息的窗口ID (作用好像不大 在消息分发的时候并没有用到它)
code是这个通知的 消息类型 对应 wNotifyCode 所以很重要
下面是消息分发过程 可以看到 pNMHDR->hwndFrom != NULL 不能为空 否则直接goto到程序结束
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
LRESULT lResult = 0;
// 。。。
// special case for notifies
if (message == WM_NOTIFY)
{
NMHDR* pNMHDR = (NMHDR*)lParam; //这里可以看到pNMHDR->hwndFrom 不可以为空
if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
goto LReturnTrue;
return FALSE;
}
//。。。
}
在这里可以看到 UINT_PTR nID = _AfxGetDlgCtrlID(hWndCtrl); 拿到的消息控件的id 但是整个过程没有用到NMHDR::idFrom ,所以应该可以根据需要自定义这个数值了。
BOOL CWnd::OnNotify(WPARAM, LPARAM lParam, LRESULT* pResult)
{
ASSERT(pResult != NULL);
NMHDR* pNMHDR = (NMHDR*)lParam;
HWND hWndCtrl = pNMHDR->hwndFrom;
// get the child ID from the window itself
UINT_PTR nID = _AfxGetDlgCtrlID(hWndCtrl);
int nCode = pNMHDR->code;
ASSERT(hWndCtrl != NULL);
ASSERT(::IsWindow(hWndCtrl));
if (_afxThreadState->m_hLockoutNotifyWindow == m_hWnd)
return TRUE; // locked out - ignore control notification
// reflect notification to child window control
if (ReflectLastMsg(hWndCtrl, pResult))
return TRUE; // eaten by child
AFX_NOTIFY notify;
notify.pResult = pResult;
notify.pNMHDR = pNMHDR;
return OnCmdMsg((UINT)nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, NULL);
}
发送过程
那么根据已有的基础知识就可以调用sendmessage 模拟发送WM_NOTIFY 消息了 注好像不能用PostMessage发送
NMHDR nMHDR;
nMHDR.code= HDN_USERMSG;
nMHDR.hwndFrom =this->GetSafeHwnd();
nMHDR.idFrom = NULL;//用户可以自定义
result= GetParent()->SendMessage(WM_NOTIFY,NULL/*这个参数也为用到被舍弃了*/,(LPARAM)&nMHDR);
响应消息
ON_NOTIFY(pNMHDR->code, /*AfxGetDlgCtrlID(pNMHDR->hwndFrom) 这里当然不能这么写 只是告诉你它应该来自哪里*/, memberFxn)
其中返回值 result就是 响应消息里LRESULT* 的数值。
补充知识点
#define ON_NOTIFY_REFLECT(wNotifyCode, memberFxn) \
{ WM_NOTIFY+WM_REFLECT_BASE, (WORD)(int)wNotifyCode, 0, 0, AfxSigNotify_v, \
(AFX_PMSG) \
(static_cast<void (AFX_MSG_CALL CCmdTarget::*)(NMHDR*, LRESULT*) > \
(memberFxn)) },
#define ON_NOTIFY(wNotifyCode, id, memberFxn) \
{ WM_NOTIFY, (WORD)(int)wNotifyCode, (WORD)id, (WORD)id, AfxSigNotify_v, \
(AFX_PMSG) \
(static_cast< void (AFX_MSG_CALL CCmdTarget::*)(NMHDR*, LRESULT*) > \
(memberFxn)) },
#define ON_NOTIFY_EX(wNotifyCode, id, memberFxn) \
{ WM_NOTIFY, (WORD)(int)wNotifyCode, (WORD)id, (WORD)id, AfxSigNotify_EX, \
(AFX_PMSG) \
(static_cast< BOOL (AFX_MSG_CALL CCmdTarget::*)(UINT, NMHDR*, LRESULT*) > \
(memberFxn)) },
#define ON_NOTIFY_REFLECT_EX(wNotifyCode, memberFxn) \
{ WM_NOTIFY+WM_REFLECT_BASE, (WORD)(int)wNotifyCode, 0, 0, AfxSigNotify_b, \
(AFX_PMSG) \
(static_cast<BOOL (AFX_MSG_CALL CCmdTarget::*)(NMHDR*, LRESULT*) > \
(memberFxn)) },
ON_NOTIFY 放置于父窗口 用于处理控件消息
ON_NOTIFY_REFLECT 是放置于控件自身内部,称为反射消息,可以形象的记忆为子控件发送给父窗口的消息又被父窗口先发送回给子控件处理的过程,用于处理自己发给父窗口的消息,而且早于父窗口的ON_NOTIFY得到消息 ,并且在控件自己处理的消息后,父窗口便接收不到ON_NOTIFY的消息了
ON_NOTIFY_REFLECT_EX是有返回值的反射消息,主要解决 控件和窗口都要响应此消息的需求,即此消息有先发送回控件,控件通过 return false;来让父窗口继续响应此消息。
ON_NOTIFY_EX 我没有测试也没有查到完整的介绍 仅有一句仅供参考(两者的区别是:ON_NOTIFY_EX允许处理几个控件,当返回值是fasle的时候可以处理几个控件的相同消息)
接着像下面这些 后缀 RANGE的就是将连续的消息放到一起处理,为了方便 功能与前面一样,这里不做过多的重复解释
#define ON_NOTIFY_RANGE(wNotifyCode, id, idLast, memberFxn)