首页 > 解决方案 > TrackMouseEvent 问题

问题描述

我有一个子类按钮,当鼠标光标悬停在它上面时,我试图突出显示它。但是,我似乎无法使该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'(灰色)表示尚未收到消息;'1'(蓝色)表示已WM_MOUSEMOVE收到;'2'(红色)表示已WM_MOUSEHOVER收到;'3' 表示已WM_MOUSELEAVE收到。

这是发生了什么:当我最初运行程序时,按钮是灰色的(没有收到鼠标消息)。按钮保持灰色,直到我将光标移到按钮上。此时,它变为绿色(表示“WM_MOUSELEAVE”)。它应该变成红色(对于“WM_MOUSEHOVER”)并且在我将光标从按钮上移开之前不会变成绿色。无论我将光标移动到哪里,该按钮现在都保持绿色。

有谁知道我做错了什么?

标签: c++windowssubclassing

解决方案


您的case块缺少breaks,因此当WM_MOUSEMOVE收到代码时,代码将通过代码 for WM_MOUSEHOVER,然后将通过代码 for WM_MOUSELEAVE,然后将通过代码 forWM_PAINT等。所以HIflag任何时候都将始终为 3跌倒WM_PAINT发生。

您还泄漏了HBRUSH返回CreateSolidBrush()的内容。DeleteObject()完成使用后,您需要刷子。

但最重要的HIflag是,它是内部的一个局部变量subSIproc(),所以当操作系统自己发出时,它总是为0 WM_PAINT,而不是当它通过时到达。

您需要在每个按钮的基础上存储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);
}

话虽如此,另一种为所有者绘制按钮的背景着色的方法是让窗口处理WM_CTLCOLORBTN消息,或者至少处理WM_DRAWITEM消息。这样,窗口可以将按钮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);
}

推荐阅读