检测到悬停 Winapi 时绘制按钮

问题描述 投票:0回答:1

我正在尝试创建一个按钮并处理可能的事件,例如单击按钮和悬停。

我找到了很多有用的文章,并且到目前为止已经能够验证事件的真实性。 我遇到的问题是绘图。

在我向您展示的代码中,我为按钮创建了一个子类,我想通过它而不是通过父窗口的

mainwindproc
处理所有事件。

最初的想法是使用

WM_DRAWITEM
WM_NOTIFY
,但这些消息被发送到父窗口而不是按钮。

回到问题,我希望在检测到悬停时按钮的颜色发生变化,因此我使用

isHover
变量来控制
WM_PAINT
中选择的颜色。

但是,尽管

isHover
的值在
WM_MOUSEHOVER
WM_MOUSELEAVE
中发生了应有的更改,但在
WM_PAINT
中并未完成此操作,您可以通过控制台上打印的文本注意到这一点。

我不知道问题是什么,希望你能帮我解决。

注意:如果任何建议能带来我想要的结果,我准备尝试任何建议,即尽可能执行与

mainwindowproc
之外的按钮相关的所有操作。

#define UNICODE
#include <windows.h>
#include <CommCtrl.h>
#include <windowsx.h>
#include <iostream>
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ButtonWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{

    
    const wchar_t g_szClassName[] = L"myWindowClass";
    WNDCLASSEX wc = {0};
    HWND hwnd;
    MSG Msg;

    
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = CreateSolidBrush(RGB(30, 30, 30));
    wc.lpszMenuName = NULL;
    wc.lpszClassName = g_szClassName;
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    
    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"فشل تسجيل فئة النافذة", L"خطأ", MB_OK);
        return 1;
    }

    
    hwnd = CreateWindowEx(0, L"MyWindowClass", L"نافذة جديدة",
                          WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                          500, 350, NULL, NULL, wc.hInstance, NULL);

    if (hwnd == NULL)
    {
        MessageBox(NULL, L"فشل إنشاء النافذة", L"خطأ", MB_OK);
        return 1;
    }
    HWND hButton1 = CreateWindowEx(0, L"BUTTON", L"button1", WS_VISIBLE | WS_CHILD | BS_FLAT,
                                   200, 200, 100, 40, hwnd, (HMENU)101, NULL, NULL);

    if (hButton1 == NULL)
    {
        MessageBox(NULL, L"فشل إنشاء الزر", L"خطأ", MB_OK);
        return 1;
    }

    SetWindowSubclass(hButton1, &ButtonWndProc, 1, 0);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    
    while (Msg.message != WM_QUIT)
    {
        if (GetMessage(&Msg, NULL, 0, 0))
        {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    }
    return 0;
}

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {

    case WM_DESTROY:
    {
        PostQuitMessage(0);
        break;
    }
    case WM_CLOSE:
    {
        
        DestroyWindow(hwnd);
        break;
    }

    default:
      
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    return 0;
}

LRESULT CALLBACK ButtonWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{

    bool isTracking;
    bool isHover;
    switch (uMsg)
    {

    case WM_NCDESTROY:
        RemoveWindowSubclass(hWnd, &ButtonWndProc, uIdSubclass);
        break;

    case WM_MOUSEMOVE:
    {
        std::cout << "WM_MOUSEMOVE RECEIVED " << std::endl;        
        std::cout << "hovered =  " << isHover << std::endl; // to check the value of isHover
        if (!isTracking)
        {
            TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT)};
            tme.dwFlags = TME_LEAVE | TME_HOVER;
            tme.hwndTrack = hWnd;  // hWnd;
            tme.dwHoverTime = 10; // milliseconds
            isTracking = TrackMouseEvent(&tme);
        }

        return 0 ;
    }
    case WM_MOUSELEAVE:
    {
        std::cout << "(" << GET_X_LPARAM(lParam) << ", " << GET_Y_LPARAM(lParam) << ")" << std::endl;
        isTracking = FALSE;
        isHover = FALSE;
        std::cout << "WM_MOUSELEAVE RECEIVED " << std::endl; 
        std::cout << "hovered =  " << isHover << std::endl; // to check the value of isHover
        InvalidateRect(hWnd, NULL, true);
        UpdateWindow(hWnd);

        return 0;
    }
    case WM_MOUSEHOVER:
    {
        std::cout << "(" << GET_X_LPARAM(lParam) << ", " << GET_Y_LPARAM(lParam) << ")" << std::endl;
        isTracking = TRUE;
        isHover = TRUE;
        std::cout << "WM_MOUSEHOVER RECEIVED " << std::endl;        
        std::cout << "hovered =  " << isHover << std::endl; // to check the value of isHover
        InvalidateRect(hWnd, NULL, true);
        UpdateWindow(hWnd);
        return 0;
    }
    case WM_PAINT:
    {

        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        HBRUSH brush;
        std::cout << "WM_PAINT RECEIVED" << std::endl;
        std::cout << "paint hovered =  " << isHover << std::endl; // to check the value of isHover when receiving WM_PAINT message

        if (isHover)
        {
            brush = CreateSolidBrush(RGB(255, 0, 0));
        }
        else
        {
            brush = CreateSolidBrush(RGB(0, 255, 0));
        }

        FillRect(hdc, &ps.rcPaint, brush);
        DeleteObject(brush);
        EndPaint(hWnd, &ps);

        break;
    }
    }

    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
c++ winapi button hover paint
1个回答
0
投票

您的变量是 ButtonWndProc 的本地变量,因此每次调用 ButtonWndProc 时它们都会丢失并重置。您需要将变量移出 ButtonWndProc,以便它们可以在消息之间保留。特别是如果您计划对多个按钮使用相同的 ButtonWndProc。

创建一个类/结构体对象实例来保存变量,然后将指向该对象的指针传递给 SetWindowSubclass,以便可以将其传递到每个消息的 ButtonWndProc 中。

© www.soinside.com 2019 - 2024. All rights reserved.