(MFC) 如果控件是私有成员,父类如何接收控件的消息?

Posted

技术标签:

【中文标题】(MFC) 如果控件是私有成员,父类如何接收控件的消息?【英文标题】:(MFC) How can a parent class receive a control's messages if the control is a private member? 【发布时间】:2012-11-21 16:36:36 【问题描述】:

假设我的主类有一个私有成员,它是从 CTreeView 控件派生的类。如何处理来自主类本身的树视图控件的消息?

这类似于 Visual Studios 为您构建的 MDI 基础应用程序,其中您有两个名为 CClassView 和 CFileView 的可停靠树视图控件,每个控件都有一个派生自 CTreeView 的私有成员。

我可以像这样将消息从子成员控件 CViewTree 传递到我的 CFileView 类吗?

void CViewTree::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult)

    GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)pNMHDR);

此代码引发异常,但如果这确实有效,我将如何处理父类中的 TVN_SELCHANGED 消息?

编辑: 所以我尝试了以下建议,但没有一个运气好。

//First try, in the parent .h file:
afx_msg BOOL OnSelChange(NMHDR *pNMHDR, LRESULT *pResult);

//In the .cpp file:
ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnSelChange)

//and

BOOL ParentClass::OnSelChange(NMHDR *pNMHDR, LRESULT *pResult)

     AfxMessageBox(L"in handler");
     Return TRUE;

第二次尝试:

//in the parent .h file:
afx_msg void OnSelChange(NMHDR *pNMHDR, LRESULT *pResult);

//In the .cpp file:
ON_NOTIFY(TVN_SELCHANGED, AFX_IDW_PANE_FIRST, OnSelChange)

//and
void  ParentClass::OnSelChange(NMHDR *pNMHDR, LRESULT *pResult)

     AfxMessageBox(L"in handler");

【问题讨论】:

【参考方案1】:

不确定为什么要这样做,因为视图和父级之间存在紧密耦合,因此代码可重用性较低。如果你想重用选择逻辑,你可以像DRAWCLI sample那样将它提取到一个单独的类中。

TVN_SELCHANGED 已发送给父级。但是,当子窗口中存在 ON_NOTIFY_REFLECT 时,MFC 的 message reflection 会将通知路由到子窗口的消息映射。

如果您希望父级在消息处理中也有发言权,您可以将 ON_NOTIFY_REFLECT 更改为 ON_NOTIFY_REFLECT_EX 并在反射消息处理程序中返回 FALSE。

您将在父级处获得 WM_NOTIFY,因此您处理通知的方式是将a ON_NOTIFY macro 添加到树形视图的父级,就像您通常对对话框上的树形控件所做的那样。如果您没有指定,视图的 ID 可能是 AFX_IDW_PANE_FIRST。

【讨论】:

优秀的文章。许多人试图“制作” MFC 已经提供的功能。 说实话我不想这样做。我想以“正确”的方式来做。我已经尝试在父母中处理 TVN_SELCHANGED,但它不起作用。当然,很可能我做错了。你能给我一个例子,你将如何在父母中处理这条消息? 您将在父级处获得一个 WM_NOTIFY,因此您要做的就是向树形视图的父级添加一个 ON_NOTIFY 宏,就像您通常对对话框上的树形控件所做的那样。如果您没有指定,视图的 ID 可能是 AFX_IDW_PANE_FIRST。 @subject_x 您应该查看答案中包含的两个链接。他们描述了消息反射及其工作原理。 谢谢,我通读了答案,甚至没有注意到链接。【参考方案2】:

Sheng 能够找出我的问题,现在回想起来是非常微不足道的。也许这会帮助其他可能有同样问题的人。

在我从 Visual Studio 2010 生成的带有 Visual Studio 风格的 MDI 程序中,CFileView 有一个 CViewTree 的子成员实例。 CViewTree 是从 CTreeCtrl 派生的。

默认情况下,MFC 已经将消息沿子到父链传递。答案是确定从父类中获取通知消息的控件 ID。

所以,首先,我们需要知道树形控件的 ID。在CFileView的OnCreate方法中,可以看到这段代码:

if (!m_wndFileView.Create(dwViewStyle, rectDummy, this, 4))

MSDN 中的 Create 方法如下:

virtual BOOL Create(
   DWORD dwStyle,
   const RECT& rect,
   CWnd* pParentWnd,
   UINT nID 
);

在我的示例中,id 为 4。现在在父级(在本例中为 CFileView)中,只需创建您的 ON_NOTIFY 宏:

BEGIN_MESSAGE_MAP(CFileView, CDockablePane)  //precreated for you
    ON_NOTIFY(TVN_SELCHANGED, 4, OnSelChanged)  //you create this
END_MESSAGE_MAP()  //precreated for you

我必须手动键入上面的行,因为父类的类向导或消息属性没有 =TVN_SELCHANGED 消息。接下来,确保您的处理程序方法 OnSelChanged 在 CFileView.h 文件中声明为:

afx_msg void OnSelChanged(NMHDR *pNMHDR, LRESULT *pResult);

现在我可以像这样处理 TVN_SELCHANGED 消息(回到 CFileView.cpp 中):

void CFileView::OnSelChanged(NMHDR *pNMHDR, LRESULT *pResult)

    HTREEITEM item = m_wndFileView.GetSelectedItem();
    AfxMessageBox(m_wndFileView.GetItemText(item));

【讨论】:

【参考方案3】:

在所描述的情况下,如果您想使用 WM_NOTIFY TVN_SELCHANGED 消息从控件 CViewTree 通知父 CFileView,您应该在虚拟 OnNotify 函数中执行此操作,而不是使用消息映射。如果 OnNotify 没有遇到正确的处理程序,消息将转到父​​ CMainFrame,在那里您可以使用消息映射。

BOOL CFileView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)

   if (nmHdr->idFrom != 4)
      return CDockablePane::OnNotify(wParam, lParam, pResult);
   if (nmHdr->code == TVN_SELCHANGED)
   
      OnItemsSelChanged((NMHDR*)lParam, pResult);
      return TRUE;
   
   return FALSE;

【讨论】:

以上是关于(MFC) 如果控件是私有成员,父类如何接收控件的消息?的主要内容,如果未能解决你的问题,请参考以下文章

MFC如何添加自定义控件

MFC 如何调用另一个控件的变量

如何在 MFC 自定义控件类中挂钩控件关闭

在 MFC 应用程序中动态创建控件

MFC(C++):为啥顶部的控件不接收事件?

如何使用 MFC 以编程方式更改 ActiveX 控件的属性?