Search code examples
c++cwinapibuttoncommon-controls

Set button text color in Win32 API using custom drawing


I wanted to create some sort of a dark-mode button using Common Controls & Win32 API.

I wanted to use custom drawing to set my button's background & text color.

As for the background, it seems to work fine, but I can't figure out how to set the text color.

Here is what I did (in the window handler function):

LRESULT CALLBACK WindowHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static HBRUSH defaultbrush = NULL;
    static HBRUSH hotbrush = NULL;
    static HBRUSH selectbrush = NULL;

    switch (msg)
    {
    case WM_CREATE:
    {
        HWND button = CreateWindowA("Button", "Click Me", WS_VISIBLE | WS_CHILD,
            10, 10, 80, 30, hwnd, (HMENU)1, NULL, NULL);
        if (!button)
        {
            MessageBoxA(NULL, "Button Creation Failed!", "Error!", MB_ICONEXCLAMATION);
            exit(EXIT_FAILURE);
        }
        break;
    }
    case WM_NOTIFY:
    {
        LPNMHDR some_item = (LPNMHDR)lParam;

        if (some_item->idFrom == 1 && some_item->code == NM_CUSTOMDRAW)
        {
            LPNMCUSTOMDRAW item = (LPNMCUSTOMDRAW)some_item;

            if (item->uItemState & CDIS_SELECTED)
            {
                //Select our color when the button is selected
                if (selectbrush == NULL)
                    selectbrush = CreateSolidBrush(0x383838);

                //Create pen for button border
                HPEN pen = CreatePen(PS_INSIDEFRAME, 0, RGB(0, 0, 0));

                //Select our brush into hDC
                HGDIOBJ old_pen = SelectObject(item->hdc, pen);
                HGDIOBJ old_brush = SelectObject(item->hdc, selectbrush);

                Rectangle(item->hdc, item->rc.left, item->rc.top, item->rc.right, item->rc.bottom);

                //Clean up
                SelectObject(item->hdc, old_pen);
                SelectObject(item->hdc, old_brush);
                DeleteObject(pen);

                //Here is the problem:
                DrawTextA(item->hdc, "Click Me", -1, &item->rc, DT_CENTER | DT_VCENTER);
                return CDRF_SKIPDEFAULT;
            }
            else
            {
                if (item->uItemState & CDIS_HOT) //Our mouse is over the button
                {
                    //Select our color when the mouse hovers our button
                    if (hotbrush == NULL)
                        hotbrush = CreateSolidBrush(0x474747);

                    HPEN pen = CreatePen(PS_INSIDEFRAME, 0, RGB(0, 0, 0));

                    HGDIOBJ old_pen = SelectObject(item->hdc, pen);
                    HGDIOBJ old_brush = SelectObject(item->hdc, hotbrush);

                    Rectangle(item->hdc, item->rc.left, item->rc.top, item->rc.right, item->rc.bottom);

                    SelectObject(item->hdc, old_pen);
                    SelectObject(item->hdc, old_brush);
                    DeleteObject(pen);

                    //Here too:
                    DrawTextA(item->hdc, "Click Me", -1, &item->rc, DT_CENTER | DT_VCENTER);
                    return CDRF_SKIPDEFAULT;
                }

                //Select our color when our button is doing nothing
                if (defaultbrush == NULL)
                    defaultbrush = CreateSolidBrush(0x383838);

                HPEN pen = CreatePen(PS_INSIDEFRAME, 0, RGB(0, 0, 0));

                HGDIOBJ old_pen = SelectObject(item->hdc, pen);
                HGDIOBJ old_brush = SelectObject(item->hdc, defaultbrush);

                Rectangle(item->hdc, item->rc.left, item->rc.top, item->rc.right, item->rc.bottom);

                SelectObject(item->hdc, old_pen);
                SelectObject(item->hdc, old_brush);
                DeleteObject(pen);

                //And also here:
                DrawTextA(item->hdc, "Click Me", -1, &item->rc, DT_CENTER | DT_VCENTER);
                return CDRF_SKIPDEFAULT;
            }
        }
        return CDRF_DODEFAULT;
        break;
    }
    case WM_DESTROY:
        DeleteObject(defaultbrush);
        DeleteObject(selectbrush);
        DeleteObject(hotbrush);
        PostQuitMessage(0);
        break;
    }
    return DefWindowProcA(hwnd, msg, wParam, lParam);
}

I tried following a different post I've found on Stack Overflow which is this one: How can I change the background color of a button WinAPI C++ But they only show how to set the background color, not the text color.

Can anyone please help me with that?


Solution

  • You just need to set the text color using the SetTextColor function, somewhere before your call to DrawText():

        //...
        SetTextColor(item->hdc, RGB(0,255,0)); // For green text, but use any COLORREF value.
        DrawTextA(item->hdc, "Click Me", -1, &item->rc, DT_CENTER | DT_VCENTER);
        return CDRF_SKIPDEFAULT;
    

    You may also need to set the background mode to TRANSPARENT, in order to keep the color previously painted:

        SetBkMode(item-hdc, TRANSPARENT);