在WM_COMMAND中调用GDI+绘制线条无效,求解决方案
点击按钮触发WM_COMMAND调用GDI+绘图不显示的问题
我尝试通过点击按钮触发WM_COMMAND消息,调用GDI+绘制一条线条,但点击按钮后线条并未显示。不过在WM_PAINT消息中绘制线条是有效的,我需要实现点击按钮就显示线条的效果。以下是我的代码:
#include <Windows.h> #include <gdiplus.h> #pragma comment(lib, "gdiplus.lib") #define onButtonClicked 1 using namespace Gdiplus; WNDCLASS NewWindowClass(HBRUSH BGColor, HCURSOR Cursor, HINSTANCE hInt, HICON Icon, LPCWSTR Name, WNDPROC Procedure); LRESULT CALLBACK mainProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow) { GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr); WNDCLASS mainClass = NewWindowClass((HBRUSH)COLOR_WINDOW, LoadCursor(NULL, IDC_ARROW), hInst, LoadIcon(NULL, IDI_APPLICATION), L"Window", mainProcedure); if (!RegisterClassW(&mainClass)) return -1; MSG mainMessage = { 0 }; CreateWindow(L"Main", L"Main window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 500, 500, 500, 400, NULL, NULL, NULL, NULL); while (GetMessage(&mainMessage, NULL, NULL, NULL)) { TranslateMessage(&mainMessage); DispatchMessage(&mainMessage); } GdiplusShutdown(gdiplusToken); return 0; } WNDCLASS NewWindowClass(HBRUSH BGColor, HCURSOR Cursor, HINSTANCE hInt, HICON Icon, LPCWSTR Name, WNDPROC Procedure) { WNDCLASS NWC = { 0 }; NWC.hbrBackground = BGColor; NWC.hCursor = Cursor; NWC.hInstance = hInt; NWC.hIcon = Icon; NWC.lpszClassName = Name; NWC.lpfnWndProc = Procedure; return NWC; } LRESULT CALLBACK mainProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; PAINTSTRUCT ps; switch (msg) { case WM_CREATE: CreateWindowA("button", "Draw", WS_VISIBLE | WS_CHILD, 10, 10, 50, 20, hWnd, (HMENU)onButtonClicked, NULL, NULL); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_COMMAND: switch (wp) { case onButtonClicked: hdc = BeginPaint(hWnd, &ps); Graphics gf(hdc); Pen pen(Color(0, 0, 0)); gf.DrawLine(&pen, 10, 160, 50, 200); EndPaint(hWnd, &ps); break; } break; default: return DefWindowProc(hWnd, msg, wp, lp); } }
问题分析
不能在WM_COMMAND中直接使用BeginPaint/EndPaint组合来绘图。BeginPaint仅在处理WM_PAINT消息时有效,它的作用是获取窗口的无效区域并初始化绘图环境;在WM_COMMAND中调用这组函数,系统不会正确渲染绘制内容——要么绘制的图形会被后续的窗口重绘操作覆盖,要么根本不会被输出到屏幕上。
GDI+函数本身可以在非WM_PAINT的消息中调用,但必须通过GetDC获取窗口DC,不过这种方式绘制的内容是临时的,窗口最小化、被遮挡恢复后会丢失,不符合持久化显示的需求。
正确实现方案
Windows的标准绘图模型要求:所有需要持久化显示的绘图操作都应该放在WM_PAINT消息处理中。点击按钮时只需要触发窗口重绘请求,让系统自动调用WM_PAINT完成绘图。具体步骤:
- 定义一个状态变量标记是否需要绘制线条
- 按钮点击时设置状态为
true,并调用InvalidateRect触发窗口重绘 - 在
WM_PAINT中根据状态变量决定是否绘制线条
修改后的完整代码
#include <Windows.h> #include <gdiplus.h> #pragma comment(lib, "gdiplus.lib") #define onButtonClicked 1 using namespace Gdiplus; WNDCLASS NewWindowClass(HBRUSH BGColor, HCURSOR Cursor, HINSTANCE hInt, HICON Icon, LPCWSTR Name, WNDPROC Procedure); LRESULT CALLBACK mainProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp); // 标记是否需要绘制线条 bool drawLine = false; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow) { GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr); WNDCLASS mainClass = NewWindowClass((HBRUSH)COLOR_WINDOW, LoadCursor(NULL, IDC_ARROW), hInst, LoadIcon(NULL, IDI_APPLICATION), L"Window", mainProcedure); if (!RegisterClassW(&mainClass)) return -1; MSG mainMessage = { 0 }; // 修正窗口类名参数,和注册的类名一致 CreateWindow(L"Window", L"Main window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 500, 500, 500, 400, NULL, NULL, hInst, NULL); while (GetMessage(&mainMessage, NULL, NULL, NULL)) { TranslateMessage(&mainMessage); DispatchMessage(&mainMessage); } GdiplusShutdown(gdiplusToken); return 0; } WNDCLASS NewWindowClass(HBRUSH BGColor, HCURSOR Cursor, HINSTANCE hInt, HICON Icon, LPCWSTR Name, WNDPROC Procedure) { WNDCLASS NWC = { 0 }; NWC.hbrBackground = BGColor; NWC.hCursor = Cursor; NWC.hInstance = hInt; NWC.hIcon = Icon; NWC.lpszClassName = Name; NWC.lpfnWndProc = Procedure; return NWC; } LRESULT CALLBACK mainProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { HDC hdc; PAINTSTRUCT ps; switch (msg) { case WM_CREATE: CreateWindowA("button", "Draw", WS_VISIBLE | WS_CHILD, 10, 10, 50, 20, hWnd, (HMENU)onButtonClicked, NULL, NULL); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); Graphics gf(hdc); if (drawLine) { Pen pen(Color(0, 0, 0)); gf.DrawLine(&pen, 10, 160, 50, 200); } EndPaint(hWnd, &ps); break; case WM_COMMAND: switch (wp) { case onButtonClicked: drawLine = true; // 触发窗口重绘,让WM_PAINT处理绘图逻辑 InvalidateRect(hWnd, NULL, TRUE); break; } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, msg, wp, lp); } return 0; }
额外修正说明
原代码中CreateWindow的第一个参数使用了L"Main",但注册的窗口类名是L"Window",这会导致窗口创建失败,修改后保持类名一致。
内容的提问来源于stack exchange,提问作者Ogurchik
相关产品推荐
相关产品推荐

