OnNotify函数 ON_NOTIFY消息总结

清泛编译

函数CWnd::OnNotify处理通知消息。其默认实现是检查通知消息处理函数的消息映射,然后调用。(checks the message map for notification handlers to call.)。

一般说来,你不用重载OnNotify。你可以写一个处理函数,然后添加消息映射(这个就不用详解了吧)。

当然,也可以通过重载OnNotify函数处理指定控件的指定消息,例如:

BOOL CNotifyDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
	NMHDR* hdr=(NMHDR*)lParam;
	if( hdr->code==NM_CLICK && hdr->idFrom==IDC_BUTTON1)
	{
		MessageBox(L"你单击了Button1",L"消息");
	}
	return CDialog::OnNotify(wParam,lParam,pResult);
}

OnNotify()不响应按钮单击事件?
 


自定义WM_NOTIFY消息

习惯了用自定义用户消息进行各种状态的通知,特别是子窗口与父窗口之间的交互。但ON_MESSAGE没有控件ID的限制,如果有多个子窗口发送同一个消息给父窗口时,父窗口就不知道哪个窗口发的(当然可以用参数进行约定)。
如何解决这个问题?
有几种思路:1.重写ON_MESSAGE宏,增加ID的限制;2.模拟按钮单击消息;3.自定义WM_NOTIFY消息。基于这些思路都不能修改MFC底层的代码。
用调试的方式查看MFC的实现代码,发现重写ON_MESSAGE宏不能实现,具有ID判断的只在WM_NOTIFY和WM_COMMAND两个消息中,两个消息最终都会进入 CCmdTarget::OnCmdMsg函数进行ID判断。在比较之下,决定使用WM_NOTIFY消息。
下面分析下WM_NOTIFY消息的路由过程。
分析 CWnd::OnWndMsg函数,在MFC中窗口消息在这边派送的。关于WM_NOTIFY消息的代码如下:
LRESULT lResult = 0;
...
// special case for notifies
if (message == WM_NOTIFY)
{
    NMHDR* pNMHDR = (NMHDR*)lParam;
    if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
    goto LReturnTrue;
    return FALSE;
}
OnNotify的定义如下:
BOOL CWnd::OnNotify(WPARAM, LPARAM lParam, LRESULT* pResult)
可以发现这边忽略了WPARAM的作用,lParam的参数实际指向NMHDR* pNMHDR信息。
在OnNotify中进过必要的处理后进入OnCmdMsg函数,在这个函数中会找相应的消息映射,并进行ID对应。如果找到会进入_AfxDispatchCmdMsg函数调用对应的消息函数。
分析到这,我们比较关心的是NMHDR结构体的定义
typedef struct tagNMHDR
{
    HWND      hwndFrom;
    UINT_PTR  idFrom;
    UINT      code;         // NM_ code
}   NMHDR;
通过分析代码发现:
hwndFrom:必须有,声明消息来自哪个窗口。
idFrom:可有可无,声明窗口ID
code:具体的子消息,即通知类型。非常关键。
 
分析到此,我们实现下自定义notify.
1.定义通知类型:
#define WM_GRID_SELECT_CHANGE(WM_USER + 1)
2.增加消息映射函数,并实现:
afx_msg void OnNotifyGridChanged(NMHDR *pNMHDR, LRESULT *pResult);
3.增加映射对应关系:
ON_NOTIFY(WM_GRID_SELECT_CHANGE, GRIDCTRL_ID, &CContradictionRuleView::OnNotifyGridChanged)
4.在子窗口中发送WM_NOTIFY消息
if (this->GetParent())
{
    NMHDR nmhdr;
    nmhdr.hwndFrom= this->m_hWnd;
    nmhdr.idFrom  = 0;//m_id;
    nmhdr.code  = WM_GRID_SELECT_CHANGE;  // 用户自定义消息

    //发送notify消息
    this->GetParent()->SendMessage(WM_NOTIFY, (WPARAM)nmhdr.idFrom,(LPARAM)&nmhdr);
}
OK到此实现了自定义的notify消息。希望对你有所帮助,如果有什么疑问请联系wjh_2010@163.com。

(声明:原文来自MSDN 2001 Oct版,原文内涉及的连接由于是脱机连接,所以译文内的连接是本人尽量找自MSDN online)
TN061: ON_NOTIFY and WM_NOTIFY Messages

这个技术文章介绍了关于新WM_NOTIFY消息, 还描述了建议使用的一种在你的MFC应用程序中处理WM_NOTIFY消息的方法。

Windows 3.x 的 通告消息

在Windows 3.x下,控件通过发送一个消息给它的父窗口来告知诸如目标点击,内容的变化与选中,控件北京绘制等等之类的事件。简单的通告消息以特殊的WM_COMMAND消息形式来发送,通知码(如BN_CLICKED)与控件ID存放在wParam里,lParam保存控件的句柄。此时注意,wParamlParam已经装满了数据,再也传递不了别的数据了,这些消息只能是简单的通告消息。举个例子,BN_CLICKED通告消息,无法发送按下鼠标按键时鼠标的位置信息。

当Windows 3.x下的控件需要发送包括额外数据的通告消息时,它们使用各种特殊目的的消息,包括WM_CTLCOLORWM_VSCROLLWM_HSCROLLWM_DRAWITEMWM_MEASUREITEM,WM_COMPAREITEMWM_DELETEITEMWM_CHARTOITEMWM_VKEYTOITEM等等。这些消息能够被反射回给发送它们的的控件,要看更多信息,查阅 TN062: Message Reflection for Windows Controls. (TN062: Windows控件的消息反射)

Win32下的通告消息

对于那些Windows 3.1的控件, Win32 API使用那些曾在Windows3.x有的绝大部分通告消息。However, Win32 also adds a number of sophisticated, complex controls to those supported in Windows 3.x.这些控件经常发送带附加数据的通告消息。设计者们没有为每一个需要附加数据的通告消息增加一个新的WM_* 消息,而是只增加了一个消息,WM_NOTIFY,这个消息可以通过一标准化格式传递任意多的额外数据。

WM_NOTIFY消息包括 保存发送消息控件ID的wParam和保存一个结构指针的lParam两部分。那个结构可以是一个NMHDR结构或者某些更大点的、以NMHDR结构为第一个成员的结构。这样的话,一个指向该结构的指针可以是NMHDR结构指针,也可以是那个更大点的结构指针,看你怎么转换他了。

在大多数情况下,那个指针会指向更大点的结构,当你用到的时候就需要转换它。 只有几个通告消息,如 common通告消息(名字以NM_开始),工具提示控件的TTN_SHOWTTN_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控件的LVN_KEYDOWN通告消息所使用的结构,在list view控件里键盘按键被按下时发送这个消息。那个指针就指向LV_KEYDOWN结构体,定义如下:

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

这样,因为NMHDR是这个结构的第一个成员,那个指针既可以转换为NMHDR型指针也可以转换为LV_KEYDOWN型指针。

(笔者注:标准命名约定,LV_KEYDOWN已经改名为:NMLVKEYDOWN。MSDN Library - October 2001上是这么说的呵呵。)

对新Windows控件通用的通告消息

一些通告消息对所有新的Windows控件来说是通用的,这些通告消息传递一个指向NMHDR结构体的指针。

Notification code Sent because
NM_CLICK 用户在控件内单击鼠标左键
NM_DBLCLK 用户在控件内双击鼠标左键
NM_RCLICK 用户在控件内单击鼠标右键
NM_RDBLCLK 用户在控件内双击鼠标右键
NM_RETURN 用户在控件具有输入焦点的时候按下回车
NM_SETFOCUS 控件获得输入焦点
NM_KILLFOCUS 控件失去输入焦点
NM_OUTOFMEMORY 控件因为没有足够的可用内存而不能完成一项操作

ON_NOTIFY: 在MFC应用程序里处理 WM_NOTIFY 消息

函数CWnd::OnNotify处理通告消息。其默认实现是检查通告消息处理函数的消息映射,然后调用。(checks the message map for notification handlers to call.)一般说来,你不用重载OnNotify。你可以写一个处理函数,然后在你自己的窗口类的消息映射表里添加一个该函数的消息映射入口。

ClassWizard,通过ClassWizard属性页或者WizardBar工具条,能够创建ON_NOTIFY消息映射入口,并且给您提供了一个处理函数的框架。更多关于通过ClassWizard使得添加消息映射更容易的信息,请看Visual C++ Programmer's Guide的 Mapping Messages to Functions 。

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

ON_NOTIFY( wNotifyCode, id, memberFxn )

斜体字的参数被替换为:

wNotifyCode

    要被处理的通告消息代码,如 LVN_KEYDOWN

id

    发送通告消息的控件ID。

memberFxn

    通告消息发送后被调用的成员函数。

你的成员函数必须按照如下形式声明:

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

斜体字参数为::

pNotifyStruct

    指向通告消息结构的指针,类型如上声明。

result

    指向函数返回之前要被设置结果值的变量指针。

代码实例

现指定你要成员函数OnKeydownList1函去处理ID为IDC_LIST1的CListCtrl控件的LVN_KEYDOWN消息,你可以通过ClassWizard把下面的内容加入到你的消息映射表里:

ON_NOTIFY( LVN_KEYDOWN, IDC_LIST1, OnKeydownList1 )

在上面的例子里,ClassWizard提供的函数是:

void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)
{
   LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;
   // TODO: Add your control notification handler
   //                 code here
   
   *pResult = 0;
}

ClassWizard自动生成合适类型的指针。你可以通过pNMHDR或者pLVKeyDow访问通告消息结构体。

ON_NOTIFY_RANGE

如果你需要处理一组控件的同一个WM_NOTIFY消息,你可使用ON_NOTIFY_RANGE而不是ON_NOTIFY。例如,你有一组按钮,想让它们对某一通告消息执行相同的动作。

When you use ON_NOTIFY_RANGE, you specify a contiguous range of child identifiers for which to handle the notification message by specifying the beginning and ending child identifiers of the range.

(不太会翻译,大意就是,使用ON_NOTIFY_RANGE的时候,要指定一个你所需要相同相同消息处理函数控件的ID范围)

ClassWizard不去处理ON_NOTIFY_RANGE的使用,要用它,就自己在消息映射表里编辑。

ON_NOTIFY_RANGE的消息映射入口与函数原型如下表示:

ON_NOTIFY_RANGE( wNotifyCodeididLastmemberFxn )

斜体字参数被替换为:

wNotifyCode

    要被处理的通告消息代码,如 LVN_KEYDOWN

id

    连续ID范围里的第一个。

idLast

    连续ID范围里的最后一个。

memberFxn

    通告消息发送后被调用的成员函数。

你的成员函数必须按照如下形式声明:

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

斜体字参数为:

id

    发送通告消息的控件ID。

pNotifyStruct

    指向通告消息结构的指针,类型如上声明。

result

    指向函数返回之前要被设置结果值的变量指针。

ON_NOTIFY_EX, ON_NOTIFY_EX_RANGE(这部分不理解,先放一放)

If you want more than one object in the notification routing to handle a message, you can use ON_NOTIFY_EX(or ON_NOTIFY_EX_RANGE) rather than ON_NOTIFY (or ON_NOTIFY_RANGE). The only difference between the EX version and the regular version is that the member function called for the EX version returns a BOOLthat indicates whether or not message processing should continue. Returning FALSE from this function allows you to process the same message in more than one object.

ClassWizard does not handle ON_NOTIFY_EX or ON_NOTIFY_EX_RANGE; if you want to use either of them, you need to edit your message map yourself.

The message-map entry and function prototype for ON_NOTIFY_EX and ON_NOTIFY_EX_RANGE are as follows. The meanings of the parameters are the same as for the non-EX versions.

ON_NOTIFY_EX( nCode, id, memberFxn )

ON_NOTIFY_EX_RANGE( wNotifyCodeididLast, memberFxn )

The prototype for both of the above is the same:

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

In both cases, id holds the child identifier of the control that sent the notification.

Your function must return TRUE if the notification message has been completely handled or FALSE if other objects in the command routing should have a chance to handle the message.

来源:http://blog.csdn.net/wzyzb/article/details/3974360

OnNotify ON_NOTIFY

分享到:
评论加载中,请稍后...
创APP如搭积木 - 创意无限,梦想即时!
回到顶部