Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to subclass a gui control?

I’m trying to learn how to subclass a GUI control and ‘modify’ her hdc.
This is my subclass callback:

//mygui.h
#include <commctrl.h> // SetWindowSubclass
#pragma comment(lib, "Comctl32.lib")

#include <windows.h> // GDI includes.
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
using namespace DllExports;
#pragma comment (lib,"Gdiplus.lib")



typedef UCHAR GuiControls;
enum GuiControlTypes {
    GUI_CONTROL_BUTTON
};

struct GuiControlOptionsType
{
    int x;
    int y;
    int width;
    int height;
    LPCWSTR text;
    GuiControlTypes controltype;

    bool ERASEDBKGND = false; // Used on the subclass proc.
    HDC dc;
};

class Gui
{
public:
    std::map<HWND, GuiControlOptionsType> control_list;
    HWND GuihWnd;
    HWND A_LasthWnd;
    LRESULT Create();
    LRESULT AddControl(GuiControls aControlType, GuiControlOptionsType opt);
};

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



//mygui.cpp

/* Window Procedure. */
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // TODO
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
{
    Gui mygui;
    mygui.Create();
}

LRESULT Gui::Create()
{
    WNDCLASSEX wc{};
    MSG Msg;
    HWND hWnd = nullptr;

    wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; 
    wc.hInstance = 0; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; 
    wc.lpszClassName = L"classname";

    if (!RegisterClassEx(&wc))
        // TODO

    this->GuihWnd = CreateWindowW(
        wc.lpszClassName,
        L"Title",
        WS_EX_COMPOSITED | WS_EX_LAYERED | // Double buffering
        WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
        CW_USEDEFAULT, CW_USEDEFAULT, 500, 200,
        nullptr, nullptr, nullptr, nullptr);

    DWORD err = GetLastError();
    if (this->GuihWnd == NULL)
        // TODO

    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    GuiControlOptionsType opt;
    opt.x = 10; opt.y = 10; opt.width = 100; opt.height = 100; opt.text = L"test";
    this->AddControl(GUI_CONTROL_BUTTON, opt);
    SetWindowSubclass(this->A_LasthWnd, ButtonProc, 1, (DWORD_PTR)this);

    ShowWindow(this->GuihWnd, SW_SHOW);
    UpdateWindow(this->GuihWnd);

    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

    return 0; // Msg.wParam;

}



LRESULT Gui::AddControl(GuiControls aControlType, GuiControlOptionsType opt)
{
    switch (aControlType)
    {
    case GUI_CONTROL_BUTTON:
    {
        HWND hWnd = CreateWindow(
            L"BUTTON",  // Predefined class; Unicode assumed 
            opt.text,   // Button text 
            WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // Styles 
            opt.x,          // x position 
            opt.y,          // y position 
            opt.width,      // Button width
            opt.height,     // Button height
            this->GuihWnd,  // Parent window
            NULL,           // No menu.
            NULL,           //(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
            NULL);          // Pointer not needed.

        opt.controltype = GUI_CONTROL_BUTTON;
        this->control_list.emplace(hWnd, opt);
        this->A_LasthWnd = hWnd;
    }
    break;

    default:
        break;
    }
    return 1;
}



LRESULT CALLBACK ButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    Gui* pThis = (Gui*)dwRefData;

    switch (uMsg)
    {
    case WM_ERASEBKGND:
    {
        if (pThis->control_list[hWnd].ERASEDBKGND)
            return 1;

        // Create/save the new button dc.
        GpBitmap* pBitmap;
        GdipCreateBitmapFromScan0(pThis->control_list[hWnd].width, pThis->control_list[hWnd].height, 0, PixelFormat32bppPARGB, 0, &pBitmap);

        GpGraphics* g;
        GdipGetImageGraphicsContext(pBitmap, &g);
        GdipGraphicsClear(g, 0xFF2400ff);

        HBITMAP hbm;
        GdipCreateHBITMAPFromBitmap(pBitmap, &hbm, 0);

        HDC dc = CreateCompatibleDC(NULL);
        SelectObject(dc, hbm);

        pThis->control_list[hWnd].dc = dc;

        GdipDisposeImage(pBitmap);
        GdipDeleteGraphics(g);
        DeleteObject(hbm);

        pThis->control_list[hWnd].ERASEDBKGND = 1;
    }
    break;

    case WM_LBUTTONDBLCLK:
    case WM_LBUTTONDOWN:
    {
        InvalidateRect(hWnd, 0, 1);
    }
    break;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);

        BLENDFUNCTION bf;
        bf.SourceConstantAlpha = 255;
        bf.BlendOp = AC_SRC_OVER;
        bf.BlendFlags = 0;
        bf.AlphaFormat = AC_SRC_ALPHA;

        GdiAlphaBlend(hdc, 0, 0, pThis->control_list[hWnd].width, pThis->control_list[hWnd].height,
            pThis->control_list[hWnd].dc, 0, 0, pThis->control_list[hWnd].width, pThis->control_list[hWnd].height, bf);

        EndPaint(hWnd, &ps);
        DeleteObject(hdc);
        return TRUE;
    }
    break;

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

The problem is… when I click on the button it restores her default dc, when I minimize/restore it draws with my ‘custom’ dc.

https://i.imgur.com/PL5BkYi.gif

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

I tried adding a call to InvalidateRect under WM_LBUTTONDOWN, but it resulted in the same thing.

I also tried creating the Gui with ‘double buffering’ adding the styles WS_EX_COMPOSITED | WS_EX_LAYERED.

What I’m missing?

>Solution :

BS_OWNERDRAW should be "her" style, I think.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading