TN061:ON_NOTIFY和WM_NOTIFY消息

注释

自联机文档中首次包含此说明以来,尚未更新以下技术说明。 因此,某些过程和主题可能过期或不正确。 有关最新信息,建议在在线文档索引中搜索您感兴趣的主题。

此技术说明提供有关新WM_NOTIFY消息的背景信息,并介绍了在 MFC 应用程序中处理WM_NOTIFY消息的建议(最常见的)方法。

Windows 3.x 中的通知消息

在 Windows 3.x 中,控件通过向父级发送消息来通知其父级事件,例如鼠标单击、内容和选择更改以及控件背景绘制。 简单通知作为特殊WM_COMMAND消息发送,通知代码(如BN_CLICKED)和打包到 wParam 中的控件 ID 以及控件的句柄在 lParam 中。 请注意,由于 wParamlParam 已满,因此无法传递任何其他数据 — 这些消息只能是简单的通知。 例如,在BN_CLICKED通知中,无法发送有关单击按钮时鼠标光标位置的信息。

当 Windows 3.x 中的控件需要发送包含其他数据的通知消息时,它们使用各种特殊用途的消息,包括WM_CTLCOLOR、WM_VSCROLL、WM_HSCROLL、WM_DRAWITEM、WM_MEASUREITEM、WM_COMPAREITEM、WM_DELETEITEM、WM_CHARTOITEM、WM_VKEYTOITEM等。 这些消息可以反映回发送它们的控件。 有关详细信息,请参阅 TN062:Windows 控件的消息反射

Win32 中的通知消息

对于 Windows 3.1 中存在的控件,Win32 API 使用 Windows 3.x 中使用的大多数通知消息。 但是,Win32 还会向 Windows 3.x 中支持的控件添加一些复杂的复杂控件。 通常,这些控件需要使用其通知消息发送其他数据。 Win32 API 的设计器选择只添加一条消息 WM_,* WM_NOTIFY,它可以以标准化方式传递任意数量的附加数据,而不是为每个需要额外数据的新通知添加新消息。

WM_NOTIFY消息包含发送 wParam 中的消息的控件的 ID,以及指向 lParam 中结构的指针。 此结构是 NMHDR 结构或具有 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消息, 代码 成员将设置为TTN_SHOW。

大多数通知将指向包含 NMHDR 结构的较大结构的指针作为其第一个成员。 例如,考虑列表视图控件的LVN_KEYDOWN通知消息所使用的结构,该消息是在列表视图控件中按下键时发送的。 指针指向 一个LV_KEYDOWN 结构,该结构定义如下:

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

请注意,由于 NMHDR 成员是此结构中的第一个成员,因此在通知消息中传递的指针可以转换为指向 NMHDR 的指针或指向 LV_KEYDOWN的指针。

所有新 Windows 控件通用的通知

某些通知适用于所有新的 Windows 控件。 这些通知将指针传递给 NMHDR 结构。

通知代码 已发送,因为
NM_CLICK 用户单击控件中的鼠标左键
NM_DBLCLK 用户双击控件中的鼠标左键
NM_RCLICK 用户在控件中单击了鼠标右键
NM_RDBLCLK 用户双击控件中的鼠标右键
NM_RETURN 用户按下了 ENTER 键,而控件具有输入焦点
NM_SETFOCUS 控件已获得输入焦点
NM_KILLFOCUS 控件丢失了输入焦点
NM_OUTOFMEMORY 控件无法完成作,因为没有足够的可用内存

ON_NOTIFY:处理 MFC 应用程序中的WM_NOTIFY消息

该函数 CWnd::OnNotify 处理通知消息。 其默认实现检查消息映射,以获取要调用的通知处理程序。 一般情况下,不会重写 OnNotify。 而是提供处理程序函数,并将该处理程序的消息映射条目添加到所有者窗口类的消息映射。

ClassWizard(通过 ClassWizard 属性表)可以创建ON_NOTIFY消息映射条目并提供框架处理程序函数。 有关使用 ClassWizard 简化此作的详细信息,请参阅 将消息映射到函数

ON_NOTIFY消息映射宏具有以下语法:

ON_NOTIFY(wNotifyCode, id, memberFxn)

其中参数为:

wNotifyCode
要处理的通知消息的代码,例如LVN_KEYDOWN。

id
为其发送通知的控件的子标识符。

memberFxn
发送此通知时要调用的成员函数。

必须使用以下原型声明成员函数:

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

其中参数为:

pNotifyStruct
指向通知结构的指针,如上述部分所述。

结果
指向在返回之前设置的结果代码的指针。

示例:

若要指定成员函数 OnKeydownList1 要处理 CListCtrl 其 ID 为 IDC_LIST1LVN_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 自动提供正确类型的指针。 可以通过 pNMHDRpLVKeyDow 访问通知结构。

ON_NOTIFY_RANGE

如果需要为一组控件处理相同的WM_NOTIFY消息,可以使用ON_NOTIFY_RANGE而不是ON_NOTIFY。 例如,你可能有一组按钮,你希望对特定通知消息执行相同的作。

使用ON_NOTIFY_RANGE时,可以通过指定范围的开始和结束子标识符来指定一个连续的子标识符范围,以处理通知消息。

ClassWizard 不处理ON_NOTIFY_RANGE;若要使用它,需要自行编辑邮件映射。

ON_NOTIFY_RANGE的消息映射条目和函数原型如下所示:

ON_NOTIFY_RANGE(wNotifyCode, id, idLast, memberFxn)

其中参数为:

wNotifyCode
要处理的通知消息的代码,例如LVN_KEYDOWN。

id
连续标识符范围内的第一个标识符。

idLast
连续标识符范围内的最后一个标识符。

memberFxn
发送此通知时要调用的成员函数。

必须使用以下原型声明成员函数:

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

其中参数为:

id
发送通知的控件的子标识符。

pNotifyStruct
指向通知结构的指针,如上所述。

结果
指向在返回之前设置的结果代码的指针。

ON_NOTIFY_EX,ON_NOTIFY_EX_RANGE

如果希望通知路由中的多个对象处理消息,可以使用ON_NOTIFY_EX(或ON_NOTIFY_EX_RANGE)而不是ON_NOTIFY(或ON_NOTIFY_RANGE)。 EX 版本和常规版本的唯一区别在于,为 EX 版本调用的成员函数返回一个 BOOL,指示消息处理是否应继续。 通过此函数返回 FALSE ,可以在多个对象中处理同一消息。

ClassWizard 不处理ON_NOTIFY_EX或ON_NOTIFY_EX_RANGE;如果要使用其中任一项,则需要自行编辑邮件映射。

ON_NOTIFY_EX和ON_NOTIFY_EX_RANGE的消息映射条目和函数原型如下所示。 参数的含义与非 EX 版本的含义相同。

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

上述两者的原型相同:

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

在这两种情况下, ID 都保留发送通知的控件的子标识符。

如果通知消息已完全处理,则函数必须返回 TRUE ;如果命令路由中的其他对象应有机会处理该消息,则函数必须返回 FALSE

另请参阅

按编号列出的技术说明
按类别列出的技术说明