我正在尝试创建一个按钮并处理可能的事件,例如单击按钮和悬停。
我找到了很多有用的文章,并且到目前为止已经能够验证事件的真实性。 我遇到的问题是绘图。
在我向您展示的代码中,我为按钮创建了一个子类,我想通过它而不是通过父窗口的
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);
}
您的变量是 ButtonWndProc 的本地变量,因此每次调用 ButtonWndProc 时它们都会丢失并重置。您需要将变量移出 ButtonWndProc,以便它们可以在消息之间保留。特别是如果您计划对多个按钮使用相同的 ButtonWndProc。
创建一个类/结构体对象实例来保存变量,然后将指向该对象的指针传递给 SetWindowSubclass,以便可以将其传递到每个消息的 ButtonWndProc 中。