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() 刷子。

但最重要的是,HIflagsubSIproc()内部的一个本地变量,所以在发出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);
}