(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) 如果控件是私有成员,父类如何接收控件的消息?的主要内容,如果未能解决你的问题,请参考以下文章