使用 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.
- Select 你的对话框 class.
- Select“属性”窗格中的消息图标。
- 在列表中找到
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 关于使用这种方法,但我发现的那些没有显示屏幕截图。
我想知道为 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.
- Select 你的对话框 class.
- Select“属性”窗格中的消息图标。
- 在列表中找到
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 关于使用这种方法,但我发现的那些没有显示屏幕截图。