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

310 Views Asked by At

I have wondered about how limited is the provided set of messages controllers for CListBox control. I am already using ON_LBN_SELCHANGE in the message map of the CDialog containing this control.

I also would like to use ON_WM_RBUTTONDOWN, so I have derived a class CMyListBox : public CListBox, so now i can map that message inside the class, but I would like to map it to the dialog in the same way of ON_LBN_SELCHANGE works.

My question is whether is possible to re-send that message to parent dialog, so I can handle it from CDialog message map, and what is the best way of doing this (send_message, post_message or is there a better notification mechanism)?

2

There are 2 best solutions below

2
On BEST ANSWER

You can set your control to send WM_PARENTNOTIFY messages to the parent dialog box (it doesn't, by default) and add an override of the OnParentNotify() function to your dialog class. The basic procedure would be as outlined below.

First, you'll need to remove the WS_EX_NOPARENTNOTIFY style from your list-box control, so that it sends the required message to its parent. There are several ways to do this but one quite easy way is to modify the "ex-style" in your OnInitDialog override, like this:

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
}

Then, you'll need to add ON_WM_PARENTNOTIFY to your dialog's message map:

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

Finally, the handler to intercept the notification:

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!
    }
}

One (potentially troublesome) issue with this approach is that (as you may have noticed) there is no information passed to the handler function about which control the notification came from. If you only have one control in your dialog that has this behaviour enabled, then that's OK; otherwise, you'll have to do some trickery (using the cursor position given and the control's window rectangle) to determine which control has sent the message.

2
On

I thought that I would also show you this approach as an alternative which would save on the need for deriving your own CListBox class.

  1. Select your dialog class.
  2. Select the Messages icon in the Properties pane.
  3. Locate WM_CONTEXTMENU in the list and add the handler via the drop-down menu.

Add Handler

The description for this handler says:

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.

You will need to uncomment the parameters that you want to use in the handler that is generated. For example:

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

Result:

enter image description here

Of-course, there is nothing wrong with deriving your own class like you have done and handling the mouse buttons and using the notify system. And I am not saying that this approach is any better. I simply show it as an alternative to detect a right-click on a control on your dialog.


Note: There are other answers here on SO about using this approach but the ones I found did not show screenshots.