如何在鼠标悬停时绘制文本控件?

How to paint a text control on mouse hover?

我在绘制静态文本控件时遇到问题。我已经阅读了一些有关此事的资料,也尝试了一些代码,但其中 none 行得通。我设法得到的最接近的是直到文本控件检测到我的鼠标悬停事件。为此,我使用控件子类化。但是,我不知道如何在鼠标悬停时绘制文本控件。

我的问题是:

  1. 我很困惑是把负责绘制文本控件的代码放在子类的WM_MOUSEMOVE还是WM_MOUSEHOVER中。原因是因为在 WM_MOUSEMOVE 中,我设置了当文本控件的矩形区域检测到光标时,应该进行绘制。同时,我觉得很奇怪,因为 WM_MOUSEHOVER 不应该已经完成​​了那个特定角色吗?

  2. 我真的需要有关如何在鼠标悬停时突出显示文本控件的帮助。

// global text control handler
HWND txtHwnd; //somewhere in the global area of codes

//class for mouse event tracking
class MouseTrackEvents
{
    bool m_bMouseTracking;

public:
    MouseTrackEvents() : m_bMouseTracking(false) {}

    void OnMouseMove(HWND hwnd)
    {
        if (!m_bMouseTracking)
        {
            // Enable mouse tracking.
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(tme);
            tme.hwndTrack = hwnd;
            tme.dwFlags = TME_HOVER;
            tme.dwHoverTime = 100; //0.1s
            TrackMouseEvent(&tme);
            m_bMouseTracking = true;
        }
    }
    void Reset(HWND hwnd)
    {
        m_bMouseTracking = false;
    }
};

//subclass proc for text control
LRESULT CALLBACK OwnerTxtProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
    //local mouse track object
    MouseTrackEvents mouseTrack;

    const int numOfMsg = 4;
    int myMsgBox[numOfMsg]; //limit to only 4

    HBRUSH hbrBkgndA = CreateSolidBrush(RGB(0, 255, 0));

    //just want to print what wparam is..
    int wmId, wmEvent;

    wchar_t wParamHI[1000];
    wchar_t wParamLO[1000];

    //for painting
    COLORREF hbrHighlight;
    //HBRUSH hbrHighlight;
    HDC txtHDC;
    RECT txtRect;

    POINT txtPt;
    RECT txtRt;

    switch (uMsg) {
        case WM_CREATE: {
            //testing purposes
            myMsgBox[0] = MessageBox(nullptr, L"Yessir!", L":D", MB_OK | MB_ICONINFORMATION);
            break;
        }

        case WM_MOUSEMOVE: {
            mouseTrack.OnMouseMove(hWnd); //activate mouse tracking
            GetWindowRect(hWnd, &txtRt); //see what value lies within

            GetCursorPos(&txtPt);

            if (PtInRect(&txtRt, txtPt)) {
                //to check whether cursor is in text rect or not
                MessageBox(nullptr, L"Okea..nice..!", L":D", MB_OK | MB_ICONINFORMATION);

                //i dont know what to do here
                //i was thinking that painting should be placed here...
                //HBRUSH hbrBkgndEdit = CreateSolidBrush(RGB(100, 100, 255));
                
            }
            else {
                //to check whether cursor is in text rect or not
                MessageBox(nullptr, L"Oh noooo!", L":D", MB_OK | MB_ICONINFORMATION);
            }

            break;
        }

        case WM_MOUSEHOVER: {
            //do something when txt ctrl is hovered
            //to test whether wm_mousehover works, and the text control is mouse hovered
            //myMsgBox[1] = MessageBox(nullptr, L"Yahooooo!", L":D", MB_OK | MB_ICONINFORMATION);
            OutputDebugString(L"--------------This text control is being hovered---------------\n");

            //testing painting when hovered
            //plan is to send message to hChild to manage the painting since it is the parent window
            //HDC theHDC = (HDC)wParam;
            //SendMessage(hWnd, WM_CTLCOLORSTATIC, (WPARAM)theHDC, (LPARAM)txtHwnd);

            //SendMessage(hWnd, WM_CTLCOLORSTATIC, (WPARAM)theHDC, lParam);

            //for painting
            //hbrHighlight = (COLORREF)(GetSysColor(COLOR_HIGHLIGHT));
            //HDC hdcStatic = (HDC)wParam;
            ////SetTextColor(hdcStatic, RGB(0, 0, 0));
            //SetBkColor(hdcStatic, hbrHighlight);

            //for painting II
            //HDC txtCtrlMe = (HDC)wParam;


            //SetTextColor(txtCtrl, RGB(0, 0, 0));
            //SetBkColor(txtCtrlMe, RGB(0, 255, 0));

            //SendMessage(hChild, WM_CTLCOLORSTATIC, (WPARAM)txtCtrlMe, (LPARAM)txtHwnd);

            //DeleteBrush(hbrBkgndA);

            mouseTrack.Reset(hWnd);
            //return (INT_PTR)hbrHighlight;

            //mouseTrack.Reset(hWnd);
            break;
            //return TRUE;
        }

        //case WM_CTLCOLORSTATIC:
        //{
        //  HDC txtCtrlMe = (HDC)wParam;

        //  //SetTextColor(txtCtrl, RGB(0, 0, 0));
        //  SetBkColor(txtCtrlMe, RGB(0, 255, 0));

        //  if (hbrBkgndA == NULL)
        //  {
        //      hbrBkgndA = CreateSolidBrush(RGB(255, 255, 255)); //eqivalent to delete brush objet
        //  }
        //  return (INT_PTR)hbrBkgndA;
        //  //break;
        //}

        //painting
        /*
            SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
            SendMessage(hChild, WM_CTLCOLORSTATIC, (WPARAM)txtCtrlMe, (LPARAM)txtHwnd);
        */

        //you can just ignore this, this is just for me to see whats inside wParam of the subclass
        case WM_LBUTTONDOWN: {
            wmId = LOWORD(wParam);
            wmEvent = HIWORD(wParam);

            int HI = swprintf_s(wParamHI, 1000, L"wParamHI: %d", wmEvent);
            MessageBox(nullptr, wParamHI, L":D", MB_OK | MB_ICONINFORMATION);

            int LO = swprintf_s(wParamLO, 1000, L"wParamLO: %d", wmId);
            MessageBox(nullptr, wParamLO, L":D", MB_OK | MB_ICONINFORMATION);
            break;
        }
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

//child window proc, which contains the static text control
LRESULT CALLBACK WndProcChild(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        //...some other codes...

        txtHwnd = CreateWindow(L"Static", L"Hello World!", WS_VISIBLE | WS_CHILD | SS_NOTIFY, 100, 100, 120, 15, hChild, (HMENU)testingTxtID, nullptr, nullptr);

        //control subclassing
        //a.k.a attaching subclass proc to the static text control
        SetWindowSubclass(txtHwnd, OwnerTxtProc, 0, 0);

        //...some other codes...

        default: {
           return DefWindowProc(hChild, message, wParam, lParam);
        }
    return 0;
}

请忽略我在WM_MOUSEHOVERWM_CTLCOLORSTATIC中注释掉的代码OwnerTxtProc(...)。它们只是我用来尝试绘制文本控件的代码,但没有用。

您的控件的所有绘制操作都应该只在 WM_CTLCOLORSTATIC 处理程序内,甚至 WM_PAINT 处理程序内。

要触发控件的重绘,只需使用 InvalidateRect().

使控件的工作区无效即可

如果要更改绘制条件,请将相关信息存储在绘制代码使用的变量中,然后在使控件失效之前更新这些变量。

尝试这样的事情:

HWND txtHwnd;
bool bMouseTracking = false;
COLORREF bkgndColor = ...; // whatever you want...
COLORREF txtColor = ...; // whatever you want...
HBRUSH hbrBkgnd = nullptr;

const UINT APP_CTLCOLORSTATIC = WM_APP + 1;

LRESULT CALLBACK StaticTxtProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
    switch (uMsg) {
        case WM_NCDESTROY: {
            RemoveWindowSubclass(hWnd, StaticTxtProc, uIdSubclass);
            if (hbrBkgnd) {
                DeleteBrush(hbrBkgnd);
                hbrBkgnd = NULL;
            }
            bMouseTracking = false;
            break;
        }

        case WM_MOUSEMOVE: {
            if (!bMouseTracking) {
                TRACKMOUSEEVENT tme = {};
                tme.cbSize = sizeof(tme);
                tme.hwndTrack = hWnd;
                tme.dwFlags = TME_HOVER;
                tme.dwHoverTime = 100; //0.1s
                bMouseTracking = TrackMouseEvent(&tme);
            }

            RECT txtRt;
            GetWindowRect(hWnd, &txtRt);

            POINT txtPt;
            GetCursorPos(&txtPt);

            if (PtInRect(&txtRt, txtPt)) {
                bkgndColor = ...; // whatever you want...
                txtColor = ...; // whatever you want...
                InvalidateRect(hWnd, NULL, TRUE);
            }

            break;
        }

        case WM_MOUSEHOVER: {
            mMouseTracking = false;
            bkgndColor = ...; // whatever you want...
            txtColor = ...; // whatever you want...
            InvalidateRect(hWnd, NULL, TRUE);
            break;
        }

        case APP_CTLCOLORSTATIC: {
            HDC hdc = reinterpret_cast<HDC>(wParam);

            SetTextColor(hdc, txtColor);
            SetBkColor(hdc, bkgndColor);

            if (hbrBkgnd)
                DeleteBrush(hbrBkgnd);
            hbrBkgnd = CreateSolidBrush(bkgndColor);

            return reinterpret_cast<LRESULT>(bkgndColor);
        }

        case WM_LBUTTONDOWN: {
            // ...
            break;
        }
    }

    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK WndProcChild(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        case WM_CREATE: {
            bkgndColor = ...; // whatever you want...
            txtColor = ...; // whatever you want...

            txtHwnd = CreateWindow(L"Static", L"Hello World!", WS_VISIBLE | WS_CHILD | SS_NOTIFY, 100, 100, 120, 15, hChild, (HMENU)testingTxtID, nullptr, nullptr);
            SetWindowSubclass(txtHwnd, StaticTxtProc, 0, 0);

            break;
        }

        case WM_CTLCOLORSTATIC: {
            return SendMessage(reinterpret_cast<HWND>(lParam), APP_CTLCOLORSTATIC, wParam, 0);
        }

        //...some other codes...

        default: {
            return DefWindowProc(hChild, message, wParam, lParam);
        }
    }

    return 0;
}

(代答题者贴出答案移至答案space).

我设法解决了我在这件事上的问题。

//global variable(s)
COLORREF bkgndColor;
COLORREF txtColor;
HBRUSH hbrBkgnd = nullptr;
const UINT APP_CTLCOLORSTATIC = WM_APP + 1;

//class for mouse event
class MouseTrackEvents
{
    bool m_bMouseTracking;

public:
    MouseTrackEvents() : m_bMouseTracking(false) {}

    void OnMouseMove(HWND hwnd)
    {
        if (!m_bMouseTracking)
        {
            // Enable mouse tracking.
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(tme);
            tme.hwndTrack = hwnd;
            tme.dwFlags = TME_HOVER | TME_LEAVE;
            tme.dwHoverTime = 10; //1ms
            TrackMouseEvent(&tme);
            m_bMouseTracking = true;
        }
    }
    void Reset(HWND hwnd)
    {
        m_bMouseTracking = false;
    }
};

//proc for static text contorl subclass
LRESULT CALLBACK OwnerTxtProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
    //local mouse track variable
    MouseTrackEvents mouseTrack;

    RECT txtRt;
    GetWindowRect(hWnd, &txtRt);

    POINT txtPt;
    GetCursorPos(&txtPt);

    switch (uMsg) {
        
        //...some other codes...

        case WM_NCDESTROY: {
            RemoveWindowSubclass(hWnd, OwnerTxtProc, uIdSubclass);
            if (hbrBkgnd) {
                DeleteBrush(hbrBkgnd);
                hbrBkgnd = NULL;
            }

            mouseTrack.Reset(hWnd);
            break;
        }

        case WM_MOUSEMOVE: {
            mouseTrack.OnMouseMove(hWnd); //activate mouse tracking

            break;
        }

        case WM_MOUSEHOVER: {
            if (PtInRect(&txtRt, txtPt)) {
                OutputDebugString(L"--------------This text control is being hovered---------------\n");
                bkgndColor = RGB(0, 0, 200); // blue
                txtColor = RGB(200, 0, 0); // red
                InvalidateRect(hWnd, NULL, FALSE);
            }
            else {
                OutputDebugString(L"--------------This text control is being left---------------\n");
                bkgndColor = RGB(100, 0, 0); // red bg
                txtColor = RGB(0, 100, 0); // green txt
                InvalidateRect(hWnd, NULL, FALSE);
            }

            mouseTrack.Reset(hWnd);

            break;
        }

        case WM_MOUSELEAVE: {
            if (!(PtInRect(&txtRt, txtPt))) {
                OutputDebugString(L"--------------This text control is being hovered---------------\n");
                bkgndColor = RGB(100, 0, 0); // red bg
                txtColor = RGB(0, 100, 0); // green txt
                InvalidateRect(hWnd, NULL, FALSE);
            }

            mouseTrack.Reset(hWnd);

            break;
        }

        case APP_CTLCOLORSTATIC: {
            HDC hdc = reinterpret_cast<HDC>(wParam);

            SetTextColor(hdc, txtColor);
            SetBkColor(hdc, bkgndColor);

            if (hbrBkgnd) {
                DeleteBrush(hbrBkgnd);
            }
            hbrBkgnd = CreateSolidBrush(bkgndColor);

            //return reinterpret_cast<LRESULT&>(bkgndColor); //--> error --> so i added '&'
            return (LRESULT)hbrBkgnd;
        }

        //...some other codes...
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

//parent of static text control
LRESULT CALLBACK WndProcChild(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    //...some other codes...

    switch (message)
    {
    case WM_CTLCOLORSTATIC:
    {
        return SendMessage(reinterpret_cast<HWND>(lParam), APP_CTLCOLORSTATIC, wParam, 0);
    }

    case WM_CREATE: //put all sorts of stuff when the context menu is called
    {
        //font to be set to
        hFont = CreateFont(17, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, TEXT("Segoe UI"));

        //set font of the textCtrl
        //doesnt work if handler target = hChild
        for (int i = 0; i < textCtrlSize; i++) {
            SendMessage(textCtrl[i], WM_SETFONT, (WPARAM)hFont, TRUE);
        }

        bkgndColor = RGB(100, 0, 0); // red bg
        txtColor = RGB(0, 100, 0); // green txt

        txtHwnd = CreateWindow(L"Static", L"Hello World!", WS_VISIBLE | WS_CHILD | SS_NOTIFY, 100, 100, 120, 30, hChild, (HMENU)testingTxtID, nullptr, nullptr);
        SetWindowSubclass(txtHwnd, OwnerTxtProc, 0, 0);

        break;
    }

    //...some other codes...

    default:
        return DefWindowProc(hChild, message, wParam, lParam);
    }
    return 0;
}