I am trying to catch the event when user releases left mouse button after clicking on listview.
当用户在单击listview后释放鼠标左键时,我试图捕获事件。
In the code below, I need to double click in order to get beep. The desired behavior is to get the beep as soon as the user releases left mouse button.
在下面的代码中,我需要双击才能发出哔哔声。一旦用户释放鼠标左键,所需的行为就是发出蜂鸣声。
Code speaks better than the words, so here is the smallest example that illustrates the problem:
代码说的比单词更好,所以这里是说明问题的最小例子:
#include
#include // various listview macros etc
#include
#include // 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(&lvi));
else // sub-item
SendDlgItemMessage(hwnd, 2000, LVM_SETITEM, 0, reinterpret_cast(&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 row in listview, no beep occurs. What am I doing wrong?
单击listview中的行后,不会发出蜂鸣声。我究竟做错了什么?
Per this discussion:
根据这个讨论:
ListView control eats mouse messages:
ListView控件吃掉鼠标消息:
...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.
...我正在对ListView进行子类化以观察WM_LBUTTONDOWN和WM_LBUTTONUP ...我的问题:我看到了WM_LBUTTONDOWN,但从未看过WM_LBUTTONUP。 Spy ++告诉我这两条消息都会转向控件。我很困惑控件如何'吃掉'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.
问题是列表控件本身设计有一些相当复杂的点击处理。当列表控件的默认windowproc收到WM_LBUTTONDOWN或WM_RBUTTONDOWN消息时,它将进入自己的模态消息循环,该循环将持续到收到相应的WM_LBUTTONUP或WM_RBUTTONUP消息。所以是的,基本上,这个模态消息循环吃了WM_LBUTTONUP / WM_RBUTTONUP消息;你的窗口代码永远不会有机会。
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.)
解决方案:如果你需要处理按钮消息,那么你也需要处理按钮消息,你的按钮消息处理程序不应该调用基本窗口proc。从本质上讲,您自己的代码需要接管所有的点击处理逻辑;因此,您的处理程序将要使用GetKeyState检查Shift和Control键的状态,并相应地选择和取消选择列表项。 (要正确处理Shift-clicks,您可以使用ListView_GetSelectionMark和ListView_SetSelectionMark获取并设置多个选择的“锚点”。)
The MSDN documentation actually mentions this:
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.
根据是否启动单击或拖动操作,以不同方式处理。要确定涉及哪个操作,列表视图控件将进入模态消息循环,直到释放按钮或移动鼠标。在单击的情况下,列表视图控件可能会更改哪个项目具有焦点以及选择了哪些项目,同时考虑了光标位置,SHIFT和CTRL键的状态等。然后列表视图控件向其父窗口发送NM_CLICK(列表视图)通知代码。
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.
如果在项目上开始拖动,则列表视图控件将选择焦点并将焦点设置为项目。然后它将LVN_BEGINDRAG通知代码发送到父窗口。父窗口负责实际执行拖动操作。
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.
处理方式与WM_LBUTTONDOWN消息相同,只是控件发送NM_RCLICK(列表视图)通知代码(而不是NM_CLICK(列表视图))和LVN_BEGINRDRAG通知代码(而不是LVN_BEGINDRAG)。请注意,该控件处理相应的WM_RBUTTONUP消息,并且不会对其进行分派。因此,即使通过子类化控件,应用程序也无法看到此消息。