鼠标在OwnerData/CustomDraw模式ListView上无WM_MOUSEMOVE消息的问题
问题:OwnerData模式下ListView无法接收WM_MOUSEMOVE消息
当鼠标位于启用LVS_OWNERDATA(OwnerData)及CustomDraw模式的ListView控件上方时,主窗口无法收到WM_MOUSEMOVE消息,但鼠标在窗体其他区域时可正常接收该消息。
完整测试代码
#define _CRT_SECURE_NO_WARNINGS #define WIN32_LEAN_AND_MEAN #pragma comment(lib, "comctl32.lib") #include <windows.h> #include <tchar.h> #include <stdio.h> #include "commctrl.h" HINSTANCE hInst; static HWND hwndListView; const wchar_t szClassName[] = L"ListViewExampleClass"; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { #ifdef _DEBUG AllocConsole();AttachConsole(GetCurrentProcessId());HWND Handle = GetConsoleWindow();freopen("CON", "w", stdout); #endif WNDCLASSEX wc; HWND hwnd; MSG Msg; InitCommonControls(); wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&wc)) { MessageBox(NULL, L"Failed to register window!", L"Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, szClassName, L"ListViewExample", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 440, 460, NULL, NULL, hInstance, NULL); if (!hwnd) { MessageBox(NULL, L"Failed to create window!", L"Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&Msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } HWND CreateListView(HINSTANCE hInstance, HWND hwndParent) { hwndListView = CreateWindowEx( WS_EX_CLIENTEDGE, // ex style "SysListView32", // class name - defined in commctrl.h TEXT(""), // dummy text WS_TABSTOP | WS_CHILD | WS_BORDER | WS_VISIBLE | LVS_REPORT | LVS_OWNERDATA, // style 0, // x position 0, // y position 400, // width 400, // height hwndParent, // parent (HMENU)2000, // ID hInst, // instance NULL); // no extra data if (!hwndListView) return NULL; //ResizeListView RECT rc; GetClientRect(hwndParent, &rc); MoveWindow(hwndListView, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); return hwndListView; } BOOL InitListColumns(HWND hwndListView) { LV_COLUMN lvColumn; int i; WCHAR szString[5][20] = { L"Main Column", L"Column 1", L"Column 2", L"Column 3", L"Column 4" }; lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvColumn.fmt = LVCFMT_LEFT; lvColumn.cx = 120; for (i = 0; i < 5; i++) { lvColumn.pszText = szString[i]; ListView_InsertColumn(hwndListView, i, &lvColumn); } return TRUE; } bool toggle = false; WNDPROC original_procedure; LRESULT CALLBACK ListViewWndProc(HWND ws, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { case WM_MOUSEMOVE: printf("LV Mouse move\n"); if (!toggle) { toggle = true; } break; case WM_MOUSELEAVE: if (toggle) printf("LV Mouse Leave\n"); break; case WM_MOUSEHOVER: if (toggle) printf("LV Mouse hOver\n"); break; } return CallWindowProc(original_procedure, ws, Message, wParam, lParam); } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static bool Tracking = false; switch (message) { case WM_CREATE: CreateListView(hInst, hWnd); InitListColumns(hwndListView); ListView_SetItemCount(hwndListView, 5); //Edit 01 - SubClass Attempt - Worked original_procedure = (WNDPROC)SetWindowLongPtr(hwndListView, GWLP_WNDPROC, (LONG_PTR)ListViewWndProc); //Edit 02 - Get/Set Capture; worked only until min/max window //SetCapture(hWnd); break; case WM_MOUSEMOVE: printf("Mouse Move %d\n", GET_X_LPARAM(lParam)); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_NOTIFY: if (((LPNMHDR)lParam)->code == LVN_GETDISPINFO) { LV_DISPINFO* lpdi = (LV_DISPINFO*)lParam; if (lpdi->item.mask & LVIF_TEXT) { WCHAR szString[7] = L"TEXT0\0"; szString[3] = (wchar_t)(L'0' + lpdi->item.iSubItem); szString[4] = (wchar_t)(L'0' + lpdi->item.iItem); wcsncpy_s(lpdi->item.pszText, lpdi->item.cchTextMax, szString, _TRUNCATE); } } //case NM_CUSTOMDRAW: // custom draw code return 0; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
编辑记录1:子类化方案尝试
最初使用SetWindowLong进行ListView子类化:
bool toggle = false; WNDPROC original_procedure; LRESULT CALLBACK ListViewWndProc(HWND ws, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { case WM_MOUSEMOVE: if (!toggle) { printf("LV Mouse move\n"); toggle = true; } break; case WM_MOUSELEAVE: if (toggle) printf("LV Mouse Leave\n"); break; case WM_MOUSEHOVER: if (toggle) printf("LV Mouse hOver\n"); break; } return CallWindowProc(original_procedure, ws, Message, wParam, lParam); }
在WM_CREATE中调用:
original_procedure = (WNDPROC)SetWindowLong(hwndListView, -4/*GWL_WNDPROC*/, (long)ListViewWndProc);
该方式对ListView无效,替换为SetWindowLongPtr后可正常捕获ListView的鼠标消息。
编辑记录2:SetCapture方案尝试
在WM_CREATE中调用SetCapture(hWnd),可让主窗口WndProc收到正确的WM_MOUSEMOVE消息,但窗口最小化/最大化后,捕获状态失效,无法继续接收ListView区域的鼠标消息。
内容的提问来源于stack exchange,提问作者USSRCoder
相关产品推荐
相关产品推荐

