listview - 我的子类过程中未收到 WM_LBUTTONUP

标签 listview winapi mouseevent

我试图捕获用户单击 ListView 后释放鼠标左键时的事件。

在下面的代码中,我需要双击才能听到蜂鸣声。所需的行为是在用户释放鼠标左键时立即发出蜂鸣声。

代码胜于 Eloquent ,所以这里是说明问题的最小示例:

#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;
}

单击 ListView 中的一行后,不会发出蜂鸣声。我做错了什么?

最佳答案

根据此讨论:

ListView control eats mouse messages :

...I am sub-classing the ListView to watch for WM_LBUTTONDOWN and WM_LBUTTONUP... My problem: I see the WM_LBUTTONDOWN, but never the WM_LBUTTONUP. Spy++ tells me that both messages are going to the control. I am baffled as to how the control 'eats up' the WM_LBUTTONUP.

...

The problem is that the list control itself is designed with some fairly sophisticated click-handling. When the list control's default windowproc receives a WM_LBUTTONDOWN or WM_RBUTTONDOWN message, it goes into a modal message loop of its own, which lasts until the corresponding WM_LBUTTONUP or WM_RBUTTONUP message is received. So yes, essentially, this modal message loop eats the WM_LBUTTONUP/WM_RBUTTONUP message; your window code never gets a chance at it.

The solution to this: If you need to handle the button-up messages, then you need to handle the button-down messages as well, and your button- down message handler should not call the base window proc. Essentially, your own code will need to take over all 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 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.)

MSDN 文档实际上提到了这一点:

Default List-View Message Processing :

WM_LBUTTONDOWN

Processed in different ways depending on whether a click or drag operation is being initiated. To determine which operation is involved, the list-view control enters a modal message loop until either the button is released or the mouse is moved. In the case of a click, the list-view control might change which item has the focus and which items are selected, taking into account the cursor position, the state of the SHIFT and CTRL keys, and so on. Then the list-view control sends its parent window an NM_CLICK (list view) notification code.

If dragging begins over an item, the list-view control selects and sets the focus to the item. Then it sends an LVN_BEGINDRAG notification code to the parent window. The parent window is responsible for actually carrying out the drag operation.

If dragging begins over the window background, the list-view control enters another modal message loop, enabling the user to form a rectangle by dragging the mouse. Items within the rectangle are selected.

...

WM_RBUTTONDOWN

Processed the same way as the WM_LBUTTONDOWN message, except that the control sends an NM_RCLICK (list view) notification code (instead of NM_CLICK (list view)) and an LVN_BEGINRDRAG notification code (instead of LVN_BEGINDRAG). Note that the control processes the corresponding WM_RBUTTONUP message, and does not dispatch it. Applications thus cannot see this message, even by subclassing the control.

关于listview - 我的子类过程中未收到 WM_LBUTTONUP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30204761/

相关文章:

JAVA - 单击鼠标时获取鼠标位置

android - 使线性布局像 ListView 中的列表项一样可选择(Android)

java - Android:ListView 上的 Sqlite

c++ - 更改 Windows 资源管理器中的当前路径

C++ Wifi 信号强度

JavaFX 鼠标拖动事件未触发

android - 异步任务 Android 中的 java.util.concurrent.RejectedExecutionException

android - 从 ListView 中删除所选项目

delphi - 如何使一个窗口停留在所有其他窗口的下方(“最底部”)

javascript - 在 800 x 600 上跟踪鼠标移动并在 1024 x 768 上显示