【问题标题】:Detect when a child CButton control is added to CDialog using WM_PARENTNOTIFY检测何时使用 WM_PARENTNOTIFY 将子 CButton 控件添加到 CDialog
【发布时间】:2020-02-04 12:33:51
【问题描述】:

合作:

  • Visual Studio 2017;
  • MFC、C++。

我正在尝试修改 MFC 项目,以便 CDialog 派生类检测何时将子控件添加到其中(CButton 派生类)。最好获取这些 CButton 的句柄 (HWND) 以进一步处理它们,就像我可以使用以下代码从父对话框窗口获取一样:

HWND handleParent = ::GetTopWindow(this->GetSafeHwnd());

我已经阅读了一种方法,通过处理 WM_PARENTNOTIFY,但我无法使用主事件函数以任何方式触发它:OnParentNotify(或 @ 987654326@ 来自某些来源)。

我已经完成了以下操作,至少对于OnParentNotify

  1. 添加了消息导出:

ON_WM_PARENTNOTIFY()

  1. 在成员函数DerivedDialog::OnInitDialog() 中从所有可能的控制句柄中删除WS_EX_NOPARENTNOTIFY 样式,在CDialog::OnInitDialog() 代码行之后:
CDialog::OnInitDialog();

HWND hwnd = ::GetTopWindow(this->GetSafeHwnd());
while (hwnd)
{
    LONG lExStyle;
    lExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);

    if (lExStyle & WS_EX_NOPARENTNOTIFY)
    {
        lExStyle &= ~WS_EX_NOPARENTNOTIFY;
        SetWindowLong(hwnd, GWL_EXSTYLE, lExStyle);
    }

    hwnd = ::GetNextWindow(hwnd, GW_HWNDNEXT);
}
  1. 声明和定义函数只是为了查看它是否被调用:
OnParentNotify(UINT message, LPARAM lParam)
{

CDialog::OnParentNotify(message, lParam)
{
    switch (LOWORD(message))
    {
        case WM_CREATE:
        {
            int a = 3;
            int b = 2;
        }
        break;
        case WM_PARENTNOTIFY:
        {
            int c = 1;
            int d = 0;
        }
    }
}

不幸的是,只有WM_CREATE 被调用一次(不要认为它是相关的或正确的,因为我有 2 个按钮必须添加到对话框中......所以如果是这种情况,我希望有 2 个 WM_CREATES ??)。

我真的不确定如何触发该消息被调用。任何提示都会非常有帮助!

【问题讨论】:

  • 如果您的控件由派生类处理,那么您可以覆盖其OnCreate(CREATESTRUCT *pCreateStruct) 成员并从那里向对话框发送一条消息(您已定义)。或者只是在那个被覆盖的函数中做你需要做的事情。新控件的WM_CREATE 消息将发送到该控件的处理程序。
  • 或者,如果您有WS_EX_PARENTNOTIFY 样式set 用于您的控件,它会将WM_PARENTNOTIFY 发送到创建和销毁对话框(测试wParam 找出哪个)。
  • 我将尝试测试WS_EX_PARENTNOTIFY 替代方案,如果我有任何结果,请告诉您。我推测 wparam 来自另一个相关函数,例如 OnParentNotify(...) ?

标签: c++ button mfc modal-dialog message


【解决方案1】:

来自MSDN documentation

系统在创建和创建时也会发送 WM_PARENTNOTIFY 消息 销毁窗口,但不销毁从对话框创建的控件 模板。系统通过指定 创建控件时的 WS_EX_NOPARENTNOTIFY 样式。一个应用程序 除非它创建自己的,否则无法覆盖此默认行为 对话框的控件。

据此,所有从对话框模板创建的按钮都不会收到WM_PARENTNOTIFY。 (DerivedDialog::OnInitDialog() 中的代码没有影响)。

如果您动态创建按钮(或子控件),WM_PARENTNOTIFY 将起作用。

示例(添加到您现有的代码中):

  1. 在对话框标题中添加CButton m_sampleButton 成员。
  2. 将创建添加到OnInitDialog代码

    m_sampleButton.Create(L"Sample", WS_CHILD|WS_VISIBLE, CRect(10, 10, 100, 100), this, 10);
    

编辑:(受@Adrian 评论启发)

另一种解决方案可能是覆盖按钮派生类的PreSubclassWindow 函数并将用户定义的消息发布到父窗口。

按钮类:

#define CUSTOM_CREATE_NOTIFY WM_USER+1001 // (add to header file)

void CCustomButton::PreSubclassWindow()
{   
    CButton::PreSubclassWindow();

    GetParent()->PostMessage(CUSTOM_CREATE_NOTIFY, (WPARAM)m_hWnd);
}

对话框类:

// add to message map
ON_MESSAGE(CUSTOM_CREATE_NOTIFY, OnCustomNotify)

LRESULT CMFCApplication2Dlg::OnCustomNotify(WPARAM wParam, LPARAM)
{   
    // wparam is the HWND to the button.

    return 0;
}

【讨论】:

  • 你好 EyIM !因此,据我了解,对话框模板基本上是我在资源文件(.rc 文件)中作为我在最终用户应用程序中使用的按钮的“描述符”所拥有的内容?还要提到我用以下交换这些按钮? :void CTestTipDialog::DoDataExchange(CDataExchange* pDX) { DerivedDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_BUTTON1, m_button); DDX_Control(pDX, IDC_BUTTON2, m_bitmapButton); }
  • 正确。一旦创建对话框,这些按钮就会自动创建。
  • 现在这会很麻烦,那么。如果成功,我将在上面尝试 Adrian 的建议并发布一个工作代码。谢谢!
  • @Adrian 和 EyIM 非常感谢您的提示。事实上,我确认 EyIM 建议它正在工作(很高兴您可以创建自定义消息来发送)。目前,此解决方案是一个不错的解决方法,我认为这是一个可行的回应。
猜你喜欢
  • 2021-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-13
  • 1970-01-01
相关资源
最近更新 更多