WM_LBUTTONUP not received in my subclass procedure

I am trying to catch an event when the user releases the left mouse button after clicking on a list box.

In the code below, I need to double click to get a beep. The desired behavior is to receive a beep as soon as the user releases the left mouse button.

The code speaks better than words, so here's the smallest example to illustrate the problem:

#include <windows.h>
#include <windowsx.h>   // various listview macros etc
#include <CommCtrl.h>
#include <stdio.h>      // swprintf_s()

// enable Visual Styles
#pragma comment( linker, "/manifestdependency:\"type='win32' \
                         name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
                         processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
                         language='*'\"")

// link with Common Controls library
#pragma comment( lib, "comctl32.lib") 

//global variables
HINSTANCE hInst;

// subclass procedure for listview -> implements drag and drop
LRESULT CALLBACK Example(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam,
    UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (message)
    {
    case WM_LBUTTONUP:    
    {
        MessageBeep(0);  // so I know  it is received
    }
        return DefSubclassProc(hwnd, message, wParam, lParam);

    case WM_NCDESTROY:
        ::RemoveWindowSubclass(hwnd, Example, 0);
        return DefSubclassProc(hwnd, message, wParam, lParam);

    }
    return ::DefSubclassProc(hwnd, message, wParam, lParam);
}

// main window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
    {
        //================ create an example listview
        RECT rec = { 0 };
        GetClientRect(hwnd, &rec);

        HWND hwndLV = CreateWindowEx(0, WC_LISTVIEW,
            L"", WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | WS_CLIPCHILDREN, 
            50, 50, 250, 200, hwnd, (HMENU)2000, hInst, 0);

        // set extended listview styles
        ListView_SetExtendedListViewStyle(hwndLV, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);

        // add some columns
        LVCOLUMN lvc = { 0 };

        lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
        lvc.fmt = LVCFMT_LEFT;

        for (long nIndex = 0; nIndex < 5; nIndex++)
        {
            wchar_t txt[50];
            swprintf_s(txt, 50, L"Column %d", nIndex);

            lvc.iSubItem = nIndex;
            lvc.cx = 60;
            lvc.pszText = txt;

            ListView_InsertColumn(hwndLV, nIndex, &lvc);
        }

        // add some items
        LVITEM lvi;

        lvi.mask = LVIF_TEXT;

        for (lvi.iItem = 0; lvi.iItem < 10000; lvi.iItem++)
        {
            for (long nIndex = 0; nIndex < 5; nIndex++)
            {
                wchar_t txt[50];
                swprintf_s(txt, 50, L"Item %d%d", lvi.iItem, nIndex);

                lvi.iSubItem = nIndex;
                lvi.pszText = txt;
                if (!nIndex)  // item 
                    SendDlgItemMessage(hwnd, 2000, LVM_INSERTITEM, 0, reinterpret_cast<LPARAM>(&lvi));
                else            // sub-item
                    SendDlgItemMessage(hwnd, 2000, LVM_SETITEM, 0, reinterpret_cast<LPARAM>(&lvi));
            }
        }

        //============================ subclass it
        SetWindowSubclass(hwndLV, Example, 0, 0);
    }
        return 0L;

    case WM_CLOSE:
        ::DestroyWindow(hwnd);
        return 0L;
    case WM_DESTROY:
    {
        ::PostQuitMessage(0);
    }
        return 0L;
    default:
        return ::DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

// WinMain

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
    int nCmdShow)
{
    // store hInstance in global variable for later use
    hInst = hInstance;

    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    // register main window class
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"Main_Window";
    wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"Window Registration Failed!", L"Error!", MB_ICONEXCLAMATION |
            MB_OK);

        return 0;
    }

    // initialize common controls
    INITCOMMONCONTROLSEX iccex;
    iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    iccex.dwICC = ICC_LISTVIEW_CLASSES;
    InitCommonControlsEx(&iccex);

    // create main window
    hwnd = CreateWindowEx(0, L"Main_Window", L"Listview Drag and Drop",
        WS_OVERLAPPEDWINDOW,
        50, 50, 400, 400, NULL, NULL, hInstance, 0);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

    return Msg.wParam;
}

      

After clicking on a line in the list, no beeps are heard. What am I doing wrong?

+3


source to share


1 answer


In this discussion:

The ListView control uses mouse messages :

... I am subclassing the ListView to view WM_LBUTTONDOWN and WM_LBUTTONUP ... My problem: I see WM_LBUTTONDOWN, but never WM_LBUTTONUP. Spy ++ tells me that both messages go to the control. I am puzzled as to how the control "eats" WM_LBUTTONUP.

...

The problem is that the list control itself is quite complex click handling. When the default list control windowproc receives a WM_LBUTTONDOWN or WM_RBUTTONDOWN message, it enters a modal message loop that lasts until the corresponding WM_LBUTTONUP or WM_RBUTTONUP message is received. So yes, essentially, this modal message loop uses the WM_LBUTTONUP / WM_RBUTTONUP message; your window code never gets a chance at it .

Solution: if you need to handle button messages, then you also need to handle button messages, and your button - down message handler should not invoke the underlying proc window. Essentially, your own code will need to handle all of the click handling logic; So your handler will want to check the state of the Shift and Control keys using GetKeyState, and select and deselect the list items accordingly. (To handle Shift-clicks correctly, you can use ListView_GetSelectionMark and ListView_SetSelectionMark to get and set the anchor point for multiple selections.)



The MSDN documentation mentions the following:

Default message handling :

WM_LBUTTONDOWN

Processed differently depending on whether a click or drag operation is triggered. To determine which operation is involved, the list control enters a modal message loop until the button is released or the mouse is moved. When clicked, a list control can change which item has focus and which items are selected, taking into account the position of the cursor, the state of the SHIFT and CTRL keys, and so on. The list-view control then sends the notification code NM_CLICK (list view) to the parent window.

If the drag starts over an item, the list control selects and sets focus to the item. It then sends the LVN_BEGINDRAG notification code to the parent window. The parent window is responsible for actually performing the drag-and-drop operation.

If dragging starts over the background of the window, the list control introduces another loop of the modal message, allowing the user to form a rectangle by dragging the mouse. Items within the rectangle are selected.

...

WM_RBUTTONDOWN

Processed the same as the WM_LBUTTONDOWN message , except that the control sends the notification code NM_RCLICK (list view) (instead of NM_CLICK (list view)) and the notification code LVN_BEGINRDRAG (instead of LVN_BEGINDRAG). Note that the control processes the appropriate WM_RBUTTONUP message and does not send it. Thus, applications cannot see this message, even by subclassing the control.

+3


source







All Articles