Как изменить цвет фона кнопки WinAPI c++

Я искал это много раз, но все, что я нахожу, это MFC. Я хочу его в C++ WinAPI. Я знаю, как изменить стиль элемента управления button, но я не могу узнать, как сделать кнопку другого цвета. Итак, как я могу изменить цвет фона элемента управления кнопки WinAPI с помощью C++? Я не хочу делать это с файлом ресурсов.

спасибо!

4 ответов


вместо ссылки я просто опубликую копию с другого поста, используя пользовательский рисунок, похожий на alwayslearningnewstuff пример:

Nothing is selectedFirst button is selected and was pushedSecond button was pushed and the mouse is over it (notice the increse of brightness - cutom hilight)

первое изображение показывает, когда ничего не выбрано, второе показывает, когда первая кнопка выбрана и была нажата, а последняя показывает, когда вторая кнопка была нажата, и мышь находится над ней (обратите внимание на увеличение яркости - cutom hilight). Чтобы сделать это, вы должны поймать сообщение NM_CUSTOMDRAW и кнопку paint самостоятельно. И вот как ты это делаешь. Также добавлена функция градиентной кисти и некоторые комментарии.

#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 

#include <Windows.h>
#include <Commctrl.h>

#define IDC_EXIT_BUTTON 101
#define IDC_PUSHLIKE_BUTTON 102

HBRUSH CreateGradientBrush(COLORREF top, COLORREF bottom, LPNMCUSTOMDRAW item)
    {
        HBRUSH Brush = NULL;
        HDC hdcmem = CreateCompatibleDC(item->hdc);
        HBITMAP hbitmap = CreateCompatibleBitmap(item->hdc, item->rc.right-item->rc.left, item->rc.bottom-item->rc.top);
        SelectObject(hdcmem, hbitmap);

        int r1 = GetRValue(top), r2 = GetRValue(bottom), g1 = GetGValue(top), g2 = GetGValue(bottom), b1 = GetBValue(top), b2 = GetBValue(bottom);
        for(int i = 0; i < item->rc.bottom-item->rc.top; i++)
        { 
            RECT temp;
            int r,g,b;
            r = int(r1 + double(i * (r2-r1) / item->rc.bottom-item->rc.top));
            g = int(g1 + double(i * (g2-g1) / item->rc.bottom-item->rc.top));
            b = int(b1 + double(i * (b2-b1) / item->rc.bottom-item->rc.top));
            Brush = CreateSolidBrush(RGB(r, g, b));
            temp.left = 0;
            temp.top = i;
            temp.right = item->rc.right-item->rc.left;
            temp.bottom = i + 1; 
            FillRect(hdcmem, &temp, Brush);
            DeleteObject(Brush);
        }
        HBRUSH pattern = CreatePatternBrush(hbitmap);

        DeleteDC(hdcmem);
        DeleteObject(Brush);
        DeleteObject(hbitmap);

        return pattern;
    }

LRESULT CALLBACK MainWindow(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static HBRUSH defaultbrush = NULL;
    static HBRUSH hotbrush = NULL;
    static HBRUSH selectbrush = NULL;
    static HBRUSH push_uncheckedbrush = NULL;
    static HBRUSH push_checkedbrush = NULL;
    static HBRUSH push_hotbrush1 = NULL;
    static HBRUSH push_hotbrush2 = NULL;
    switch (msg)
    {
        case WM_CREATE:
            {
                HWND Exit_Button = CreateWindowEx(NULL, L"BUTTON", L"EXIT", 
                                                        WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 
                                                        50, 50, 100, 100, hwnd, (HMENU)IDC_EXIT_BUTTON, NULL, NULL);
                if(Exit_Button == NULL)
                    {
                        MessageBox(NULL, L"Button Creation Failed!", L"Error!", MB_ICONEXCLAMATION);
                        exit(EXIT_FAILURE);
                    }

                HWND Pushlike_Button = CreateWindowEx(NULL, L"BUTTON", L"PUSH ME!", 
                                                        WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX | BS_PUSHLIKE, 
                                                        200, 50, 100, 100, hwnd, (HMENU)IDC_PUSHLIKE_BUTTON, NULL, NULL);
                if(Pushlike_Button == NULL)
                    {
                        MessageBox(NULL, L"Button Creation Failed!", L"Error!", MB_ICONEXCLAMATION);
                        exit(EXIT_FAILURE);
                    }
            }
        break;
        case WM_COMMAND:
            {
                switch(LOWORD(wParam))
                    {
                        case IDC_EXIT_BUTTON:
                            {
                                SendMessage(hwnd, WM_CLOSE, 0, 0);
                            }
                        break;
                    }
            }
        break;
        case WM_NOTIFY:
        {
            LPNMHDR some_item = (LPNMHDR)lParam;

            if (some_item->idFrom == IDC_EXIT_BUTTON && 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 = CreateGradientBrush(RGB(180, 0, 0), RGB(255, 180, 0), item);

                    //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);

                    //If you want rounded button, then use this, otherwise use FillRect().
                    RoundRect(item->hdc, item->rc.left, item->rc.top, item->rc.right, item->rc.bottom, 5, 5);

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

                    //Now, I don't want to do anything else myself (draw text) so I use this value for return:
                    return CDRF_DODEFAULT;
                    //Let's say I wanted to draw text and stuff, then I would have to do it before return with
                    //DrawText() or other function and 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 = CreateGradientBrush(RGB(255, 230, 0), RGB(245, 0, 0), item);

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

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

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

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

                        return CDRF_DODEFAULT;
                    }

                    //Select our color when our button is doing nothing
                    if (defaultbrush == NULL)
                        defaultbrush = CreateGradientBrush(RGB(255, 180, 0), RGB(180, 0, 0), item);

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

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

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

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

                    return CDRF_DODEFAULT;
                }
            }
            else if (some_item->idFrom == IDC_PUSHLIKE_BUTTON && some_item->code == NM_CUSTOMDRAW)
            {
                LPNMCUSTOMDRAW item = (LPNMCUSTOMDRAW)some_item;

                if (IsDlgButtonChecked(hwnd, some_item->idFrom))
                {
                    if (item->uItemState & CDIS_HOT)
                    {

                        if (push_hotbrush1 == NULL)
                            push_hotbrush1 = CreateGradientBrush(RGB(0, 0, 245), RGB(0, 230, 255), item);

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

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

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

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

                        return CDRF_DODEFAULT;
                    }


                    if (push_checkedbrush == NULL)
                        push_checkedbrush = CreateGradientBrush(RGB(0, 0, 180), RGB(0, 222, 200), item);


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


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


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


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


                    return CDRF_DODEFAULT;
                }
                else
                {
                    if (item->uItemState & CDIS_HOT)
                    {
                        if (push_hotbrush2 == NULL)
                            push_hotbrush2 = CreateGradientBrush(RGB(255, 230, 0), RGB(245, 0, 0), item);

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

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

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

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

                        return CDRF_DODEFAULT;
                    }

                    if (push_uncheckedbrush == NULL)
                        push_uncheckedbrush = CreateGradientBrush(RGB(255, 180, 0), RGB(180, 0, 0), item);

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

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

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

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

                    return CDRF_DODEFAULT;
                }
            }
            return CDRF_DODEFAULT;
        }
        break;
        case WM_CTLCOLORBTN: //In order to make those edges invisble when we use RoundRect(),
            {                //we make the color of our button's background match window's background
                return (LRESULT)GetSysColorBrush(COLOR_WINDOW+1);
            }
        break;
        case WM_CLOSE:
            {
                DestroyWindow(hwnd);
                return 0;
            }
        break;
        case WM_DESTROY:
            {
                DeleteObject(defaultbrush);
                DeleteObject(selectbrush);
                DeleteObject(hotbrush);
                DeleteObject(push_checkedbrush);
                DeleteObject(push_hotbrush1);
                DeleteObject(push_hotbrush2);
                DeleteObject(push_uncheckedbrush);
                PostQuitMessage(0);
                return 0;
            }
        break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG msg;
    const wchar_t ClassName[] = L"Main_Window";

    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = MainWindow;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW+1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = ClassName;
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    if(!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"Window Registration Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK);
        exit(EXIT_FAILURE);
    }

    hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, ClassName, L"Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 368, 248, NULL, NULL, hInstance, NULL);

    if(hwnd == NULL)
    {
        MessageBox(NULL, L"Window Creation Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
        exit(EXIT_FAILURE);
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

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

    return msg.message;
}

Я не помню ссылку на исходный код, но код bellow помог мне в прошлом решить проблему, с которой вы сейчас сталкиваетесь.

обратите внимание, что у него нет файла ресурсов, как вы просили, и находится в обычном Win32 API.

внимательно изучите его, все прокомментировано оригинальным автором.

надеюсь, это поможет вам, как это помогло мне в прошлом.

Если у вас есть какие-либо вопросы, спрашивайте, я постараюсь ответить их.

есть 4 способа, насколько я знаю, изменить цвет кнопки:

  1. владелец draw ( очевидное решение ).

  2. пользовательский draw ( на мой взгляд самое лучшее решение ).

  3. наследование управление (мне это не нравится, но возможно).

  4. использовать рисунки как ваши кнопки фон.

  5. обращение WM_CTLCOLORBTN :

из MSDN:

только владелец-drawn кнопки отвечают на обработку родительского окна сообщение.

выделено мной. если вы планируете использовать эту опцию, читайте Примечания тщательно.

код ниже демонстрирует случаи 1, 2 и 4.

#pragma comment(linker, "/manifestdependency:\"type='win32' \
    name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
    processorArchitecture='*' \
    publicKeyToken='6595b64144ccf1df' language='*'\"")

#pragma comment(lib, "comctl32.lib")

#include <windows.h>
#include <commctrl.h>

ATOM RegisterWndClass(HINSTANCE hInst);

BOOL CreateMainWnd(HINSTANCE hInstance, int nCmdShow);

LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

HINSTANCE hInst;

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hInstPrev, LPWSTR lpszCmdLine, 
    int nCmdShow)
{
    INITCOMMONCONTROLSEX icex = {0};
    icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icex.dwICC  = ICC_LISTVIEW_CLASSES | ICC_USEREX_CLASSES | ICC_BAR_CLASSES |
                  ICC_COOL_CLASSES | ICC_TAB_CLASSES | ICC_WIN95_CLASSES | 
                  ICC_PROGRESS_CLASS | ICC_PAGESCROLLER_CLASS;

    InitCommonControlsEx(&icex);

    MSG msg;

    hInst = hInstance;

    if (!RegisterWndClass(hInstance))
       return NULL;

    if (!CreateMainWnd(hInstance, nCmdShow))
       return NULL;

    while (GetMessage(&msg, NULL, NULL, NULL))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
};

ATOM RegisterWndClass(HINSTANCE hInstance)
{

    WNDCLASS wndClass = {0};
    wndClass.style = CS_DBLCLKS;
    wndClass.lpfnWndProc = MainWndProc;
    wndClass.hInstance = hInstance;
    wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = L"MainClass";
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;

    return RegisterClass(&wndClass);
 }

BOOL CreateMainWnd(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd = CreateWindow(L"MainClass", L"Buttons sample",
         WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
         GetSystemMetrics(SM_CXSCREEN) / 2 - 115,
         GetSystemMetrics(SM_CYSCREEN) / 2 - 50,
         230, 100, NULL, NULL, hInstance, NULL);

    if (!hWnd)
        return FALSE;

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

HBITMAP hBitmap = NULL;

LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
        {
            // Owner draw button

            CreateWindow(L"BUTTON", L"", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | 
                BS_OWNERDRAW, 10, 10, 60, 30, hWnd, 
                (HMENU)10001, hInst, NULL);

            // Custom draw button

            CreateWindow(L"BUTTON", L"", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 80, 
                10, 60, 30, hWnd, (HMENU)10002, hInst, NULL);

            // Bitmap button

            HWND hBitmapButton = CreateWindow(L"BUTTON", L"", WS_CHILD | WS_VISIBLE 
                | BS_PUSHBUTTON | BS_BITMAP,
                150, 10, 60, 30, hWnd,
                (HMENU)10003, hInst, NULL);

            HDC hDC = GetDC(hWnd);

            HDC hMemDC = CreateCompatibleDC(hDC);

            hBitmap = CreateCompatibleBitmap(hDC, 55, 25);

            SelectObject(hMemDC, hBitmap);

            SetDCBrushColor(hMemDC, RGB(0, 0, 255));

            RECT r = {0};
            r.left = 0;
            r.right = 55;
            r.top = 0;
            r.bottom = 25;

            FillRect(hMemDC, &r, (HBRUSH)GetStockObject(DC_BRUSH));

            DeleteDC(hMemDC);
            ReleaseDC(hWnd, hDC);

            SendMessage(hBitmapButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, 
                (LPARAM)hBitmap);

            return 0;
        }

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case 10001:
            MessageBox(hWnd, L"Owner draw button clicked", L"Message", NULL);
            return 0;
        case 10002:
            MessageBox(hWnd, L"Custom draw button clicked", L"Message", NULL);
            return 0;
        case 10003:
            MessageBox(hWnd, L"Bitmap button clicked", L"Message", NULL);
            return 0;
        }
        break;

    // Owner draw button

    case WM_DRAWITEM:
        if (wParam == 10001)
        {
            LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;

            SetDCBrushColor(lpDIS -> hDC, RGB(255, 0, 0));

            SelectObject(lpDIS -> hDC, GetStockObject(DC_BRUSH));

            RoundRect(lpDIS -> hDC, lpDIS -> rcItem.left, lpDIS -> rcItem.top,
                lpDIS -> rcItem.right, lpDIS -> rcItem.bottom, 5, 5);

            return TRUE;
        }
        break;

    // Custom draw button

    case WM_NOTIFY:
        switch (((LPNMHDR)lParam) -> code)
        {
        case NM_CUSTOMDRAW:
            if (((LPNMHDR)lParam) -> idFrom == 10002)
            {
                LPNMCUSTOMDRAW lpnmCD = (LPNMCUSTOMDRAW)lParam;

                switch (lpnmCD -> dwDrawStage)
                {
                case CDDS_PREPAINT:

                    SetDCBrushColor(lpnmCD -> hdc, RGB(0, 255, 0));
                    SetDCPenColor(lpnmCD -> hdc, RGB(0, 255, 0));
                    SelectObject(lpnmCD -> hdc, GetStockObject(DC_BRUSH));
                    SelectObject(lpnmCD -> hdc, GetStockObject(DC_PEN));

                    RoundRect(lpnmCD -> hdc, lpnmCD -> rc.left + 3, 
                        lpnmCD -> rc.top + 3, 
                        lpnmCD -> rc.right - 3, 
                        lpnmCD -> rc.bottom - 3, 5, 5);

                    return TRUE;
                }
            }
            break;
        }
        break;

    case WM_DESTROY:
        if (hBitmap != NULL)
            DeleteObject((HBITMAP)hBitmap);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

вы можете изменить кнопку (которая имеет флаг BS_OWNERDRAW) в сообщении WM_DRAWITEM на DialogProc (MSDN о WM_DRAWITEM), вот простой пример того, как нарисовать простую кнопку:

LPDRAWITEMSTRUCT Item;
    Item = (LPDRAWITEMSTRUCT)lParam;
    SelectObject(Item->hDC, CreateFont(16, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Arial Black"));
    FillRect(Item->hDC, &Item->rcItem, CreateSolidBrush(0));
    SelectObject(Item->hDC, CreateSolidBrush(0));
    if (Item->itemState & ODS_SELECTED)
    {
        SetTextColor(Item->hDC, 0);
        SelectObject(Item->hDC, CreateSolidBrush(0xFF00));
        SelectObject(Item->hDC, CreatePen(PS_SOLID, 2, 0xFF00));
    }
    else
    {
        SetTextColor(Item->hDC, 0x00FF00);  
        SelectObject(Item->hDC, CreatePen(PS_SOLID, 2, 0x00FF00));

    }
    SetBkMode(Item->hDC, TRANSPARENT);
    RoundRect(Item->hDC, Item->rcItem.left, Item->rcItem.top, Item->rcItem.right, Item->rcItem.bottom, 20, 20);
    int len;
    len = GetWindowTextLength(Item->hwndItem);
    LPSTR lpBuff;
    lpBuff = new char[len+1];
    GetWindowTextA(Item->hwndItem, lpBuff, len+1);
    DrawTextA(Item->hDC, lpBuff, len, &Item->rcItem, DT_CENTER);

вам понадобится владелец-выписанные кнопку для этого. По какой-то причине, в отличие от других элементов управления, обычные кнопки не реагируют на изменения в WM_CTLCOLORBTN обработчик сообщений.