使用 ON_WM_RBUTTONDOWN 升级 MFC CListBox 控件,以便对话框可以直接处理消息

Upgrade MFC CListBox control with ON_WM_RBUTTONDOWN so dialog can handle message directly

我想知道为 CListBox 控制提供的消息控制器集有多有限。我已经在包含此控件的 CDialog 的消息映射中使用了 ON_LBN_SELCHANGE

我也想使用 ON_WM_RBUTTONDOWN,所以我导出了一个 class CMyListBox : public CListBox,所以现在我可以在 class 中映射该消息,但我想映射它以与 ON_LBN_SELCHANGE 相同的方式进入对话框。

我的问题是是否可以将该消息重新发送到父对话框,以便我可以从 CDialog 消息映射中处理它,最好的方法是什么(send_message , post_message 还是有更好的通知机制)?

您可以将控件设置为将 WM_PARENTNOTIFY 消息发送到父对话框(默认情况下不会),并将 OnParentNotify() function 的覆盖添加到对话框 class.基本程序如下所述。

首先,您需要从列表框控件中删除 WS_EX_NOPARENTNOTIFY 样式,以便它向其父项发送所需的消息。有几种方法可以做到这一点,但一种非常简单的方法是修改 OnInitDialog 覆盖中的“ex-style”,如下所示:

BOOL MyDialog::OnInitDialog(void)
{
    CDialog::OnInitDIalog(); // Call base class
    //...
    // Other code you need
    //
    CWnd* pList = GetDlgItem(IDC_MYLISTBOX); // Use the resource ID of the listbox
    LONG_PTR exstyle = GetWindowLongPtr(pList->m_hWnd, GWL_EXSTYLE);
    // Remove the WS_EX_NOPARENTNOTIFY bit ...
    exstyle &= ~WS_EX_NOPARENTNOTIFY;
    SetWindowLongPtr(pList->m_hWnd, GWL_EXSTYLE, exstyle); // Set new style
    //...
    return TRUE; // Assuming you don't explicitly set the focus
}

然后,您需要将 ON_WM_PARENTNOTIFY 添加到对话框的消息映射中:

BEGIN_MESSAGE_MAP(MyDialog, CDialog)
    //...
    ON_WM_PARENTNOTIFY()
END_MESSAGE_MAP()

最后,拦截通知的处理程序:

void MyDialog::OnParentNotify(UINT message, LPARAM lParam)
{
    CDialog::OnParentNotify(message, lParam); // Always best to call base class for MFC!
    if (message == WM_RBUTTONDOWN) {
        // Here, we can handle the right-button click!
        // lParam will be the cursor position (x in LOWORD and y in HIWORD)
        // NOTE: See the caveat mentioned below!
    }
}

此方法的一个(可能很麻烦的)问题是(正如您可能已经注意到的那样)没有信息传递给处理函数,说明通知来自哪个控件。如果您的对话框中只有一个控件启用了此行为,那没关系;否则,您将不得不采取一些技巧(使用给定的光标位置和控件的 window 矩形)来确定哪个控件发送了消息。

我想我也会向您展示这种方法作为替代方法,这样可以节省您自己的推导 CListBox class.

  1. Select 你的对话框 class.
  2. Select“属性”窗格中的消息图标。
  3. 在列表中找到 WM_CONTEXTMENU 并通过下拉菜单添加处理程序。

这个处理程序的描述是:

Notifies a window that the user desires a context menu to appear. The user may have clicked the right mouse button (right-clicked) in the window, pressed Shift+F10 or pressed the applications key (context menu key) available on some keyboards.

wParam - A handle to the window in which the user right-clicked the mouse. This can be a child window of the window receiving the message. For more information about processing this message, see the Remarks section.

您需要取消注释要在生成的处理程序中使用的参数。例如:

void CMFCApplication4Dlg::OnContextMenu(CWnd* pWnd, CPoint /*point*/)
{
    if (pWnd->GetSafeHwnd() == m_lbList.GetSafeHwnd())
    {
        AfxMessageBox(_T("User right-clicked on the listbox control"));
    }
}

结果:

当然,像您所做的那样派生您自己的 class 并处理鼠标按钮和使用通知系统并没有错。我并不是说这种方法更好。我只是将它展示为检测对话框上控件右键单击的替代方法。


注意: SO 上还有其他 answers 关于使用这种方法,但我发现的那些没有显示屏幕截图。