¿Cómo puedo cambiar el color de fondo de un botón WinAPI C ++?

Lo he buscado muchas veces, pero todo lo que encuentro es MFC. Lo quiero en C ++ WinAPI. Sé cómo cambiar el estilo de un control de botón, pero no puedo descubrir cómo hacer que un botón tenga un color diferente. Entonces, ¿cómo puedo cambiar el color de fondo de un botón de control WinAPI usando C ++? No quiero hacer esto con un archivo de recursos.

¡Gracias!

En lugar de un enlace, simplemente publicaré una copia de mi otra publicación utilizando un dibujo personalizado, similar al ejemplo de alwayslearningnewstuff :

Nada es seleccionadoEl primer botón es seleccionado y fue empujadoSe presionó el segundo botón y el mouse está sobre él (observe el aumento de brillo, la claridad de cortejo)

La primera imagen muestra cuando no se selecciona nada, la segunda muestra cuando se selecciona el primer botón y se empuja y la última muestra cuando se presionó el segundo botón y el mouse está sobre él (observe el aumento de brillo – luz de corte). Para hacerlo, debe capturar el mensaje NM_CUSTOMDRAW y pintar el botón usted mismo. Y así es como lo haces. También se agregó la función de pincel degradado y algunos comentarios.

 #pragma comment(linker,"\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #include  #include  #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; } 

No recuerdo el enlace del código original, pero el código de abajo me ayudó en el pasado a resolver el problema que enfrenta actualmente.

Tenga en cuenta que no tiene un archivo de recursos, como lo ha solicitado, y está en una API simple de Win32.

Estudie con cuidado, todo es comentado por el autor original.

Espero que te ayude, ya que me ayudó en el pasado.

Si tiene alguna pregunta, pregunte, intentaré responderla.

Hay 4 formas, hasta donde yo sé, para cambiar el color del botón:

  1. Drenaje del propietario (solución obvia).

  2. Dibujo personalizado (en mi opinión, la mejor solución).

  3. Subclases del control (no me gusta, pero es posible).

  4. Usa bitmaps como fondo de tus botones.

  5. Manejo de WM_CTLCOLORBTN :

Desde MSDN:

solo los botones dibujados por el propietario responden a la ventana primaria que procesa este mensaje.

El énfasis es mío Si planea usar esta opción, lea atentamente la sección de Comentarios .

El siguiente código muestra los casos 1, 2 y 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  #include  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); } 

Puede editar un botón (que tiene el indicador BS_OWNERDRAW) en el mensaje WM_DRAWITEM en el DialogProc ( MSDN About WM_DRAWITEM ), aquí hay un ejemplo simple de cómo dibujar un botón simple:

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

Necesitas un botón dibujado por el propietario para eso. Por alguna razón, a diferencia de otros controles, los botones normales no reactjsn a los cambios realizados en el controlador de mensajes WM_CTLCOLORBTN .