【发布时间】:2016-09-24 05:15:25
【问题描述】:
启用禁用的子窗口后,我尝试在 WM_ENABLE 中打开鼠标跟踪,仅当鼠标光标悬停在使用 TrackMouseEvent() 且 TRACKMOUSEEVENT 结构的 dwFlags 设置为 TME_LEAVE 的窗口上时。 TrackMouseEvent() 返回 TRUE,但在调用它之后我立即收到 WM_MOUSELEAVE 消息。这仅在 2 个条件下发生。在第一个条件下,将光标移到子窗口之外,按 Enter 键禁用窗口,然后将光标移到子窗口上并按 Space 键。在第二种情况下,将光标移到窗口上,按 Enter 键禁用它,然后在按 Space 键之前将光标移动 1 个像素或更多,然后按 Space 键。如果您重新测试第二个条件,而不是在按 Space 键之前移动光标,如果您在按 Enter 键后立即按 Space 键,则鼠标跟踪将正确打开。我已经非常努力地解决了这个问题,但到目前为止我还不是很幸运。有人可以修复此代码并解释为什么在我尝试打开鼠标跟踪时会取消它吗?
#include <windows.h>
const WCHAR g_szChildClassName[] = L"Childclass////";
HINSTANCE g_hInst;
LRESULT CALLBACK ChildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static BOOL bMouseTracking = FALSE;
switch(msg)
{
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
if(hdc)
{
HBRUSH hbr = CreateSolidBrush(bMouseTracking?RGB(255, 0, 0):RGB(0, 0, 255));
if(hbr)
{
FillRect(hdc, &ps.rcPaint, hbr);
DeleteObject(hbr);
}
EndPaint(hwnd, &ps);
}
}
break;
case WM_MOUSEMOVE:
if(!bMouseTracking)
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
bMouseTracking = TrackMouseEvent(&tme);
InvalidateRect(hwnd, 0, TRUE);
}
break;
case WM_MOUSELEAVE:
bMouseTracking = FALSE;
InvalidateRect(hwnd, 0, TRUE);
break;
case WM_ENABLE:
if(wParam)
{
RECT rc;
if(GetWindowRect(hwnd, &rc))
{
POINT pt;
if(GetCursorPos(&pt))
if(PtInRect(&rc, pt))
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
//TrackMouseEvent() posts WM_MOUSELEAVE if conditions 1 and 2 are met, even though I'm trying to turn
//mouse tracking on and the cursor is over the child window. It doesn't make sense
//The problems is this piece of code right here /* bMouseTracking = TrackMouseEvent(&tme); */
//It should turn tracking on but it doesn't it cancels it even though WS_DISABLED has already been removed
//at this point
bMouseTracking = TrackMouseEvent(&tme);
InvalidateRect(hwnd, 0, TRUE);
}
}
} else {
if(bMouseTracking) {
////////If you comment everything from here ...
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE | TME_CANCEL;
tme.hwndTrack = hwnd;
//if(TrackMouseEvent(&tme)) PostMessage(hwnd, WM_MOUSELEAVE, 0, 0); //Commented this line out to do things a bit differently with the same results
if(TrackMouseEvent(&tme)) { //If this succeeds it means mouse tracking was canceled
bMouseTracking = FALSE;
InvalidateRect(hwnd, 0, TRUE);
}
////////all the way down to here the result is the same
//If you comment everything in this block out then you have another problem which can be tested with this condition:
//With window enabled move mouse over window, then press the ENTER key. The color should change
//from red to blue but it doesn't. It will change to blue though if you move the mouse 1 or more pixels after you've pressed the ENTER key
}
}
break;
case WM_DESTROY:
bMouseTracking = FALSE;
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hChild;
switch(msg)
{
case WM_CREATE:
hChild = CreateWindowEx(0, g_szChildClassName, 0, WS_VISIBLE | WS_CHILD, 4, 4, 240, 80, hwnd, 0, g_hInst, 0);
break;
case WM_KEYDOWN:
if(wParam == VK_SPACE) EnableWindow(hChild, TRUE);
else if(wParam == VK_RETURN) EnableWindow(hChild, FALSE);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
const TCHAR szClassName[] = L"abccccccc";
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = szClassName;
if(!RegisterClassEx(&wc)) return 0; //Register main window class
SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hInstance = hInstance;
wc.lpfnWndProc = ChildProc;
wc.lpszClassName = g_szChildClassName;
if(!RegisterClassEx(&wc)) return 0; //Register child window class
g_hInst = hInstance;
hwnd = CreateWindowEx(0, szClassName, L"Test", WS_OVERLAPPEDWINDOW, 40, 40, 420, 200, 0, 0, hInstance, 0);
if(!hwnd) return 0;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg, 0, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
编辑:您看不到图片中的光标,因为我使用了屏幕截图并且它没有捕获光标。在第一张图片中,光标在子窗口之外,在第二张图片中,光标在子窗口内
【问题讨论】:
-
也许不是很清楚。这段代码肯定有一个错误,wParam == 0 的 WM_ENABLE 处理程序应该不在禁用的窗口上调用 TrackMouseEvent(),当然也不应该调用 PostMessage。已经有一个 TrackMouseEvent() 处于活动状态,它会在窗口被禁用时自动生成 WM_MOUSELEAVE。什么都不做。
-
@HansPassant 我打电话给
TrackMouseEvent()将dwFlags设置为TME_LEAVE | TME_CANCEL以取消鼠标跟踪。可以调用TrackMouseEvent()来设置和取消鼠标跟踪。如果鼠标正在被跟踪,窗口禁用后不会自动取消。如果在窗口被禁用后鼠标移动 1 个或更多像素,它将被取消。即使我在wParam == 0时什么都不做,结果也是一样的。当 wParam == 0 时,将 WM_ENABLE 中的所有内容注释掉并自己查看 -
做任何你需要做的事情,这样你就不会收到 两个 WM_MOUSELEAVE 消息。
-
WM_ENABLE在窗口仍处于启用/禁用状态时发送,因此EnableWindow在发送WM_ENABLE后可能会触发WM_MOUSELEAVE。与其在WM_ENABLE处理程序中直接调用TrackMouseEvent,不如向自己发布一条自定义消息并在其相应的处理程序中调用TrackMouseEvent。 -
我尝试发布
WM_MOUSEMOVE,但系统在WM_MOUSEMOVE之后发布了WM_MOUSELEAVE。当 wParam != 0 不起作用时,似乎调用TrackMouseEvent()从WM_ENABLE跟踪鼠标