TrackMouseEvent 问题
TrackMouseEvent Problems
我有一个子类按钮,我试图在鼠标光标悬停在它上面时突出显示它。但是,我似乎无法使 TrackMouseEvent()
函数正常工作。这是创建子类的代码:
hBtn = CreateWindow(L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hWnd, HMENU(400), hInst, NULL);
SetWindowSubclass(hBtn[0], subSIproc, 400, 0);
这是子类程序:
LRESULT CALLBACK subSIproc(HWND hButton, UINT iMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRefData){
int HIflag=0;
switch(iMsg)
{
case WM_MOUSEMOVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
HIflag = 1;
RedrawWindow(hButton, NULL, NULL, RDW_INVALIDATE);
}
case WM_MOUSEHOVER:
{
HIflag = 2;
RedrawWindow(hButton, NULL, NULL, RDW_INVALIDATE);
}
case WM_MOUSELEAVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE | TME_CANCEL;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
HIflag = 3;
RedrawWindow(hButton, NULL, NULL, RDW_INVALIDATE);
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hButton, &ps);
if(HIflag==0) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x007F7F7F));
if(HIflag==1) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x00FF0000));
if(HIflag==2) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x000000FF));
if(HIflag==3) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x0000FF00));
EndPaint(hButton, &ps);
}
}
return DefSubclassProc(hButton, iMsg, wParam, lParam);
}
变量“HIflag”有四个可能的值,用于确定正在接收哪些消息,以及何时——'0'(灰色)表示尚未接收到任何消息; WM_MOUSEMOVE
收到“1”(蓝色);收到 WM_MOUSEHOVER
的“2”(红色); WM_MOUSELEAVE
收到“3”。
这是正在发生的事情:当我最初 运行 程序时,按钮是灰色的(没有收到鼠标消息)。该按钮保持灰色,直到我将光标移到该按钮上。那时,它变成绿色(表示“WM_MOUSELEAVE”)。它应该变成红色(对于“WM_MOUSEHOVER”)并且在我将光标从按钮上移开之前不会变成绿色。无论我将光标移到哪里,按钮现在都保持绿色。
有谁知道我做错了什么?
您的 case
块缺少 break
,因此当收到 WM_MOUSEMOVE
时,代码将下降到 WM_MOUSEHOVER
的代码,然后下降一直到 WM_MOUSELEAVE
的代码,然后将下降到 WM_PAINT
的代码,等等。因此,当发生任何下降到 WM_PAINT
的代码时,HIflag
将始终为 3。
您还泄露了 CreateSolidBrush()
returns 的 HBRUSH
。使用完后,您需要 DeleteObject()
刷子。
但最重要的是,HIflag
是subSIproc()
内部的一个本地变量,所以在发出WM_PAINT
时它总是0由 OS 本身,而不是由于跌落而到达。
您需要在每个按钮的基础上将 HIflag
存储在 subSIproc()
之外,例如在 HWND
内部使用 (Get|Set)WindowLongPtr(GWL_USERDATA)
、(Get|Set)Prop()
等
尝试更像这样的东西:
hBtn = CreateWindow(L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hWnd, HMENU(400), hInst, NULL);
SetProp(hBtn, TEXT("HIflag"), (HANDLE)0);
SetWindowSubclass(hBtn, subSIproc, 400, 0);
...
LRESULT CALLBACK subSIproc(HWND hButton, UINT iMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRefData)
{
switch (iMsg)
{
case WM_NCDESTROY:
{
RemoveProp(hButton, TEXT("HIflag"));
break;
}
case WM_MOUSEMOVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
SetProp(hButton, TEXT("HIflag"), (HANDLE)1);
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSEHOVER:
{
SetProp(hButton, TEXT("HIflag"), (HANDLE)2);
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSELEAVE:
{
// tracking is cancelled automatically when this message is generated!
SetProp(hButton, TEXT("HIflag"), (HANDLE)3);
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hButton, &ps);
COLORREF color;
switch ( (int) GetProp(hButton, TEXT("HIflag")) ) {
case 1: color = RGB(0x00, 0x00, 0xFF); break;
case 2: color = RGB(0xFF, 0x00, 0x00); break;
case 3: color = RGB(0x00, 0xFF, 0x00); break;
default: color = RGB(0x7F, 0x7F, 0x7F); break;
}
HBRUSH hBrush = CreateSolidBrush(color);
FillRect(hdc, &ps.rcPaint, hBrush);
DeleteObject(hBrush);
EndPaint(hButton, &ps);
return 0;
}
}
return DefSubclassProc(hButton, iMsg, wParam, lParam);
}
话虽如此,为自绘按钮的背景着色的另一种方法是让 parent window 处理 WM_CTLCOLORBTN
message, or at least the WM_DRAWITEM
消息。这样,parent window 可以将按钮的 HWND
及其关联的 HIFlag
一起存储在某个数组中,而不必跟踪标志在 HWND
本身内部。
例如:
Buttons[index].hWnd = CreateWindow(L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hWnd, HMENU(index+1), hInst, NULL);
Buttons[index].HIflag = 0;
SetWindowSubclass(hBtn, subSIproc, index+1, 0);
...
LRESULT CALLBACK ParentWndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg)
{
case WM_CTLCOLORBTN:
{
HWND hBtn = (HWND) lParam;
for (int index = 0; index < NumberOfButtons; ++index)
{
if (Buttons[index].hWnd == hBtn)
{
COLORREF color;
switch ( Buttons[index].HIflag ) {
case 1: color = RGB(0x00, 0x00, 0xFF); break;
case 2: color = RGB(0xFF, 0x00, 0x00); break;
case 3: color = RGB(0x00, 0xFF, 0x00); break;
default: color = RGB(0x7F, 0x7F, 0x7F); break;
}
return CreateSolidBrush(color);
}
}
break;
}
// or:
case WM_DRAWITEM:
{
DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*) lParam;
COLORREF color;
switch ( Buttons[dis->CtlID-1].HIflag ) {
case 1: color = RGB(0x00, 0x00, 0xFF); break;
case 2: color = RGB(0xFF, 0x00, 0x00); break;
case 3: color = RGB(0x00, 0xFF, 0x00); break;
default: color = RGB(0x7F, 0x7F, 0x7F); break;
}
HBRUSH hBrush = CreateSolidBrush(color);
FillRect(dis->hDC, &(dis->rcItem), hBrush);
DeleteObject(hBrush);
return TRUE;
}
}
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
LRESULT CALLBACK subSIproc(HWND hButton, UINT uiMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRefData)
{
switch (uiMsg)
{
case WM_MOUSEMOVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
Buttons[uId-1].HIflag = 1;
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSEHOVER:
{
Buttons[uId-1].HIflag = 2;
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSELEAVE:
{
// tracking is cancelled automatically when this message is generated!
Buttons[uId-1].HIflag = 3;
InvalidateRect(hButton, NULL, TRUE);
break;
}
}
return DefSubclassProc(hButton, uiMsg, wParam, lParam);
}
我有一个子类按钮,我试图在鼠标光标悬停在它上面时突出显示它。但是,我似乎无法使 TrackMouseEvent()
函数正常工作。这是创建子类的代码:
hBtn = CreateWindow(L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hWnd, HMENU(400), hInst, NULL);
SetWindowSubclass(hBtn[0], subSIproc, 400, 0);
这是子类程序:
LRESULT CALLBACK subSIproc(HWND hButton, UINT iMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRefData){
int HIflag=0;
switch(iMsg)
{
case WM_MOUSEMOVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
HIflag = 1;
RedrawWindow(hButton, NULL, NULL, RDW_INVALIDATE);
}
case WM_MOUSEHOVER:
{
HIflag = 2;
RedrawWindow(hButton, NULL, NULL, RDW_INVALIDATE);
}
case WM_MOUSELEAVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE | TME_CANCEL;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
HIflag = 3;
RedrawWindow(hButton, NULL, NULL, RDW_INVALIDATE);
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hButton, &ps);
if(HIflag==0) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x007F7F7F));
if(HIflag==1) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x00FF0000));
if(HIflag==2) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x000000FF));
if(HIflag==3) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x0000FF00));
EndPaint(hButton, &ps);
}
}
return DefSubclassProc(hButton, iMsg, wParam, lParam);
}
变量“HIflag”有四个可能的值,用于确定正在接收哪些消息,以及何时——'0'(灰色)表示尚未接收到任何消息; WM_MOUSEMOVE
收到“1”(蓝色);收到 WM_MOUSEHOVER
的“2”(红色); WM_MOUSELEAVE
收到“3”。
这是正在发生的事情:当我最初 运行 程序时,按钮是灰色的(没有收到鼠标消息)。该按钮保持灰色,直到我将光标移到该按钮上。那时,它变成绿色(表示“WM_MOUSELEAVE”)。它应该变成红色(对于“WM_MOUSEHOVER”)并且在我将光标从按钮上移开之前不会变成绿色。无论我将光标移到哪里,按钮现在都保持绿色。
有谁知道我做错了什么?
您的 case
块缺少 break
,因此当收到 WM_MOUSEMOVE
时,代码将下降到 WM_MOUSEHOVER
的代码,然后下降一直到 WM_MOUSELEAVE
的代码,然后将下降到 WM_PAINT
的代码,等等。因此,当发生任何下降到 WM_PAINT
的代码时,HIflag
将始终为 3。
您还泄露了 CreateSolidBrush()
returns 的 HBRUSH
。使用完后,您需要 DeleteObject()
刷子。
但最重要的是,HIflag
是subSIproc()
内部的一个本地变量,所以在发出WM_PAINT
时它总是0由 OS 本身,而不是由于跌落而到达。
您需要在每个按钮的基础上将 HIflag
存储在 subSIproc()
之外,例如在 HWND
内部使用 (Get|Set)WindowLongPtr(GWL_USERDATA)
、(Get|Set)Prop()
等
尝试更像这样的东西:
hBtn = CreateWindow(L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hWnd, HMENU(400), hInst, NULL);
SetProp(hBtn, TEXT("HIflag"), (HANDLE)0);
SetWindowSubclass(hBtn, subSIproc, 400, 0);
...
LRESULT CALLBACK subSIproc(HWND hButton, UINT iMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRefData)
{
switch (iMsg)
{
case WM_NCDESTROY:
{
RemoveProp(hButton, TEXT("HIflag"));
break;
}
case WM_MOUSEMOVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
SetProp(hButton, TEXT("HIflag"), (HANDLE)1);
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSEHOVER:
{
SetProp(hButton, TEXT("HIflag"), (HANDLE)2);
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSELEAVE:
{
// tracking is cancelled automatically when this message is generated!
SetProp(hButton, TEXT("HIflag"), (HANDLE)3);
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hButton, &ps);
COLORREF color;
switch ( (int) GetProp(hButton, TEXT("HIflag")) ) {
case 1: color = RGB(0x00, 0x00, 0xFF); break;
case 2: color = RGB(0xFF, 0x00, 0x00); break;
case 3: color = RGB(0x00, 0xFF, 0x00); break;
default: color = RGB(0x7F, 0x7F, 0x7F); break;
}
HBRUSH hBrush = CreateSolidBrush(color);
FillRect(hdc, &ps.rcPaint, hBrush);
DeleteObject(hBrush);
EndPaint(hButton, &ps);
return 0;
}
}
return DefSubclassProc(hButton, iMsg, wParam, lParam);
}
话虽如此,为自绘按钮的背景着色的另一种方法是让 parent window 处理 WM_CTLCOLORBTN
message, or at least the WM_DRAWITEM
消息。这样,parent window 可以将按钮的 HWND
及其关联的 HIFlag
一起存储在某个数组中,而不必跟踪标志在 HWND
本身内部。
例如:
Buttons[index].hWnd = CreateWindow(L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hWnd, HMENU(index+1), hInst, NULL);
Buttons[index].HIflag = 0;
SetWindowSubclass(hBtn, subSIproc, index+1, 0);
...
LRESULT CALLBACK ParentWndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg)
{
case WM_CTLCOLORBTN:
{
HWND hBtn = (HWND) lParam;
for (int index = 0; index < NumberOfButtons; ++index)
{
if (Buttons[index].hWnd == hBtn)
{
COLORREF color;
switch ( Buttons[index].HIflag ) {
case 1: color = RGB(0x00, 0x00, 0xFF); break;
case 2: color = RGB(0xFF, 0x00, 0x00); break;
case 3: color = RGB(0x00, 0xFF, 0x00); break;
default: color = RGB(0x7F, 0x7F, 0x7F); break;
}
return CreateSolidBrush(color);
}
}
break;
}
// or:
case WM_DRAWITEM:
{
DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*) lParam;
COLORREF color;
switch ( Buttons[dis->CtlID-1].HIflag ) {
case 1: color = RGB(0x00, 0x00, 0xFF); break;
case 2: color = RGB(0xFF, 0x00, 0x00); break;
case 3: color = RGB(0x00, 0xFF, 0x00); break;
default: color = RGB(0x7F, 0x7F, 0x7F); break;
}
HBRUSH hBrush = CreateSolidBrush(color);
FillRect(dis->hDC, &(dis->rcItem), hBrush);
DeleteObject(hBrush);
return TRUE;
}
}
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
LRESULT CALLBACK subSIproc(HWND hButton, UINT uiMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRefData)
{
switch (uiMsg)
{
case WM_MOUSEMOVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
Buttons[uId-1].HIflag = 1;
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSEHOVER:
{
Buttons[uId-1].HIflag = 2;
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSELEAVE:
{
// tracking is cancelled automatically when this message is generated!
Buttons[uId-1].HIflag = 3;
InvalidateRect(hButton, NULL, TRUE);
break;
}
}
return DefSubclassProc(hButton, uiMsg, wParam, lParam);
}