c++ 如何使用WM_CTLCOLORBTN更改按钮的颜色?

umuewwlo  于 2023-02-17  发布在  其他
关注(0)|答案(1)|浏览(357)

我对C++和Windows API还很陌生。
我想不出如何使用WM_CTLCOLORBTN更改按钮的背景色,同时在按钮上有位图。当我尝试这样做时,程序崩溃。
我创建了一个按钮,如下所示:

HINSTANCE hInstance = GetModuleHandle(NULL);
HWND hbutton = CreateWindow(L"BUTTON", NULL, WS_VISIBLE | WS_CHILD | BS_BITMAP | BS_PUSHBUTTON, 15, 202.5, 96.5, 72.5, hWnd, (HMENU)301, hInstance, NULL);

WndProc函数中,我编写了:

case WM_CTLCOLORBTN:
   switch (((LPNMHDR)lParam) -> code) 
   {
      MessageBox(hWnd, L"made it inside of the switch statement", L"Debug", 1);
   }

我也试过:

case WM_CTLCOLORBTN:
   LPNMHDR button = (LPNMHDR)lParam;
   if(button->idfrom == 301 && button->code == NM_CUSTOMDRAW)
   {
   }

此外,我还试过:

case CDDS_PREERASE:
   LPNMHDR button = (LPNMHDR)lParam;
   if(button->idfrom == 301 && button->code == NM_CUSTOMDRAW)
   {
   }

此外,我还尝试:

case WM_NOTIFY:
   MessageBox(hWnd, L"Code in wm_notify got ran", L"Debug", 1);

上面的代码不会导致消息框出现,因此假定它从未运行过。
所有这些方法都导致了程序崩溃。
我在其他一些帖子中看到人们把他们的代码放在WM_NOTIFY中,但这会导致代码根本无法运行。
有人知道我做错了什么吗?

icnyk63a

icnyk63a1#

首先,在创建系统定义的窗口类时不需要指定HINSTANCE,因此,可以将hInstance参数设置为NULL
第二,所有消息处理程序都是错误的:

  • WM_CTLCOLORBTN消息的lParam参数是HWND窗口句柄,而不是LPNMHDR结构指针。NMHDR结构仅在WM_NOTIFY消息中使用。
  • CDDS_PREERASE不是窗口消息。它是NM_CUSTOMDRAW消息使用的绘图阶段的值。CDDS_PREERASE具有数值3,该数值与WM_MOVE窗口消息的值相同,WM_MOVE窗口消息的lParam包含X/Y坐标,而不是LPNMHDR指针。
  • 大多数按钮通知使用WM_COMMAND消息传送到按钮的父窗口。只有3个按钮通知使用WM_NOTIFY消息传送到父窗口-BCN_DROPDOWNBCN_HOTITEMCHANGENM_CUSTOMDRAW。前2个不适用于您的按钮示例。第3个仅适用于您的应用具有Comctl32.dll v6 enabled的情况。在这种情况下,lParam参数是LPNMCUSTOMDRAW结构指针,而不是LPNMHDR指针。

现在,WM_CTLCOLORBTN文档说明:
参数
w参数
一个HDC,它指定按钮的显示上下文的句柄。
l参数
HWND,指定按钮的句柄。
返回值
如果应用程序处理这个消息,它必须返回一个画笔的句柄。系统使用画笔来绘制按钮的背景。
所以,不妨试试类似这样的方法:

HBRUSH hBtnBkgrnd = NULL;

...

case WM_CREATE: {
    ...
    HWND hbutton = CreateWindow(L"BUTTON", NULL, WS_VISIBLE | WS_CHILD | BS_BITMAP | BS_PUSHBUTTON, 15, 202.5, 96.5, 72.5, hWnd, (HMENU)301, hInstance, NULL);
    hBtnBkgrnd = CreateSolidBrush(RGB(...)); // <-- whatever color you want
    ...
    break;
}

case WM_DESTROY: {
    DeleteObject(hBtnBkgrnd);
    break;
}

case WM_CTLCOLORBTN: {
    HDC hdc = (HDC) wParam;
    // configure hdc as needed...
    SetTextColor(hdc, RGB(...)); // <-- whatever color you want
    return (LRESULT) hBtnBkgrnd;
}

或者:

case WM_CTLCOLORBTN: {
    HDC hdc = (HDC) wParam;
    // configure hdc as needed...
    SetTextColor(hdc, RGB(...));    // <-- whatever color you want
    SetDCBrushColor(hdc, RGB(...)); // <-- whatever color you want
    return (LRESULT) GetStockObject(DC_BRUSH);
}

但是,WM_CTLCOLORBTN文档还指出:
但是,只有所有者描述的按钮响应处理此消息的父窗口。
...
默认情况下,DefWindowProc函数为按钮选择默认系统颜色。**具有BS_PUSHBUTTONBS_DEFPUSHBUTTONBS_PUSHLIKE样式的按钮不使用返回的画笔。具有这些样式的按钮始终使用默认系统颜色绘制。**绘制按钮需要几种不同的画笔-表面、突出显示、和阴影-但WM_CTLCOLORBTN消息只允许返回一个画笔。**要为按钮提供自定义外观,请使用所有者描述的按钮。**有关详细信息,请参见Creating Owner-Drawn Controls
因此,您应该将BS_OWNERDRAW样式添加到按钮中,在这种情况下,您需要处理WM_DRAWITEM消息,而不是按照您想要的方式自定义绘制按钮。

相关问题