Search code examples
c++winapigdi+

windows.h gdi+ draw in WM_COMMAND


I'm trying to draw a line with gdi+ by clicking button using WM_COMMAND, but the line doesn't show up when I click the button. It works when I use WM_PAINT though but I need it to show up when clicking the button.

#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);
    }
}

Is it possible to use gdi+ functions in WM_COMMAND? If not, what should I use instead?


Solution

  • In WM_COMMAND, you need to save your desired drawing info (ie, the coordinates to draw from/to) and then call InvalidateRect() to trigger a new WM_PAINT, where you then use the saved info to do the actual drawing.

    Do not call InvalidateRect() in WM_PAINT, as you will cause an infinite paint loop.

    Try something like this:

    bool doDrawLine = false;
    Point lineStart, lineEnd;
    
    ...
    
    LRESULT CALLBACK mainProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
    
        switch (msg) {
            ...
    
            case WM_COMMAND: {
                switch (wp) {
                    case onButtonClicked: {
                        lineStart = Point(10, 160);
                        lineEnd = Point(50, 200);
                        doDrawLine = true;
                        InvalidateRect(hWnd, NULL, TRUE);
                        break;
                    }
                }
                break;
            }
    
            case WM_PAINT: {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hWnd, &ps);
                if (doDrawLine) {
                    Graphics gf(hdc);
                    Pen pen(Color(0, 0, 0));
                    gf.DrawLine(&pen, lineStart, lineEnd);
                }
                EndPaint(hWnd, &ps);
                break;
            }
    
            ...
        }
    }