【问题标题】:(MFC) How can a parent class receive a control's messages if the control is a private member?(MFC) 如果控件是私有成员,父类如何接收控件的消息?
【发布时间】: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");
}

【问题讨论】:

    标签: windows visual-c++ mfc


    【解决方案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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-17
        相关资源
        最近更新 更多