【问题标题】:Background combobox on gradient parent window?渐变父窗口上的背景组合框?
【发布时间】:2016-10-05 01:23:41
【问题描述】:

我有渐变背景的窗口。组合框有自己的背景画笔。 如何删除组合框中的白角?我该如何更换刷子或其他方式。 图片白角上用红框标记。

我将组合框创建为:

DWORD dwStyle = WS_CHILD | CBS_DROPDOWNLIST;
if (m_bVisible) dwStyle |= WS_VISIBLE;
m_hWnd = CreateWindow(WC_COMBOBOX, NULL, dwStyle,
    m_posX, m_posY, m_width, m_height, m_hParent, (HMENU)m_id, m_hInstance, NULL);

我尝试用消息WM_CTLCOLOREDIT更改背景画笔,但没有效果:

case WM_CTLCOLOREDIT:
    if ((HWND)lParam == m_hSrcListBox)
    {
        return (LRESULT)m_hBrush;
    }
break;

=== 已解决。工作版本 ===

第一种方式。

在父 WndProc 中:

case WM_CTLCOLORSTATIC:
    if ((HWND)lParam == m_hSrcListBox)
    {
        return (LRESULT)m_pSrcListBox->GetHbrush();
    }
break;

在我的课堂上:

//
// CListBox::GetHbrush().
//
// Get brush.
//
HBRUSH CListBox::GetHbrush()
{
    if (!m_hBrush)
    {
        m_hBrush = CreateTransparentBackgroundBrush(m_hParent, m_hWnd);
    }
    return m_hBrush;
}

创建透明背景:

//
// CListBox::CreateTransparentBackgroundBrush().
//
// Create transparent background for element.
//
HBRUSH CListBox::CreateTransparentBackgroundBrush(HWND parent, HWND client)
{
    RECT rct;
    POINT p1;
    POINT p2;
    GetWindowRect(client, &rct);
    p1.x = rct.left;
    p1.y = rct.top;
    ScreenToClient(parent, &p1);
    p2.x = rct.right;
    p2.y = rct.bottom;
    ScreenToClient(parent, &p2);

    HDC hdcParent = GetDC(parent);
    HDC hdcClient = GetDC(client);

    HDC hdcmem = CreateCompatibleDC(hdcClient);
    HBITMAP hbitmap = CreateCompatibleBitmap(hdcClient, p2.x - p1.x, p2.y - p1.y);
    SelectObject(hdcmem, hbitmap);
    BitBlt(hdcmem, 0, 0, p2.x - p1.x, p2.y - p1.y, hdcParent, p1.x, p1.y, SRCCOPY);

    HBRUSH pattern = CreatePatternBrush(hbitmap);

    DeleteDC(hdcmem);
    DeleteObject(hbitmap);
    ReleaseDC(client, hdcClient);
    ReleaseDC(parent, hdcParent);

    return pattern;
}

第二种方式。

在 WM_ERASEBKGND 消息中的父 WndProc 中绘制背景,然后角不会。

case WM_ERASEBKGND:
    m_hdc = (HDC)wParam;

    // draw background.

    return TRUE;
break;

两种方法的结果:

【问题讨论】:

  • 我相信这些是由WM_CTLCOLORBTN处理的?
  • 不,WM_CTLCOLORBTN 未发送
  • 你应该试试GetDlgCtrlID((HWND)lParam)
  • 有什么用?上面我写的,WM_CTLCOLOREDIT 事件中画笔返回,但系统没有使用。

标签: c++ windows user-interface winapi combobox


【解决方案1】:

对于对话框,处理 WM_CTLCOLORDLG 并为组合框返回背景画笔

如果您在对话框中显示此组合框,诀窍实际上是在对话框的窗口过程中处理WM_CTLCOLORDLG 消息。作为对该消息的响应,您返回一个笔刷句柄,对话框将使用该笔刷绘制其背景。

case WM_CTLCOLORDLG:
{
   // NOTE: This code is wrong because it creates a new brush object each time it processes
   //       the message, which it promptly leaks. It is merely for demonstration purposes.
   //       Normally, you would create the brush once, in response to WM_INITDIALOG,
   //       cache it away, and return that same cached handle each time, finally destroying
   //       the brush in response to WM_NCDESTROY.
   HBRUSH hBrush = CreateSolidBrush(RGB(255, 120, 0));
   return reinterpret_cast<INT_PTR>(hBrush);
}

                    

这是更改对话框背景颜色的标准、文档化方法,它还解决了组合框的问题。显然,无论出于何种原因,组合框控件使用此画笔绘制其背景。我想他们在自己画画时会向父母发送WM_CTLCOLORDLG 消息。

当然,这会限制您使用 GDI 画笔的图形功能。您可以绘制任何您想要的系统色或纯色,甚至可以使用影线或图案/位图笔刷,但没有简单的方法可以创建渐变笔刷。 (GDI+ 有一个,但没有 GDI。)通常没关系——您只需在 WM_PAINT(甚至是 WM_ERASEBKGND)消息处理程序中调用 GradientFill 函数。这适用于对话框的背景,但组合框仍然使用WM_CTLCOLORDLG 返回的画笔绘制其背景,因此它的角上仍然有这 4 个点在COLOR_3DFACE 中绘制(这是默认对话框过程返回的画笔)。

                    

WM_CTLCOLORDLG 返回一个空画笔 (NULL_BRUSH/HOLLOW_BRUSH) 也不起作用。它稍微改变了外观,使得右上角和左下角像素现在填充了看起来像 COLOR_3DSKSHADOW 的东西,但它们仍然明显填充了实际背景渐变以外的颜色。

                   

因此,如果您真的希望它看起来不错,那么您只有一个选择:将句柄返回给 GDI 画笔。当然,它需要与用于绘制对话框背景的画笔相同。

如果您想要渐变填充,我能想到的唯一解决方案是使用图案/位图画笔,其中位图(DDB 或 DIB)是您的渐变。不是很好,但至少 Windows 9x 限制我们使用 8×8 模式的日子已经一去不复返了。也许比我更有创造力的人可以利用这些信息想出更好的解决方法?


对于其他窗口,处理 WM_CTLCOLORSTATIC 并为组合框返回背景画笔

所有这些都用于一个对话框。但是,如果您在标准窗口(,不是对话框)中显示组合框呢?在这种情况下永远不会发送WM_CTLCOLORDLG 消息。

相反,组合框会向其父窗口发送WM_CTLCOLORSTATIC 消息,然后使用响应该消息返回的画笔句柄来绘制其背景。

这很奇怪,我知道。我只是通过进行实证测试才偶然发现它,我不太确定其基本原理是什么。如果我不得不猜测,我会说 CBS_DROPDOWNLIST 样式使组合框不可编辑(ie,它不是真正的组合框,因为没有编辑控件),所以而不是 @987654342 @,它使用WM_CTLCOLORSTATIC。禁用的编辑框也会发送WM_CTLCOLORSTATIC,具有“正常”CBS_SIMPLECBS_DROPDOWN 样式的禁用组合框也会发送。

更奇怪的是,这在启用 Aero 主题(Vista 和 7)时发生。它不会发生在 Windows 10、Luna 主题(XP 下的 Visual Styles)或 Classic 主题中。 (我没有在 Windows 8 或 8.1 上进行测试。)我想这并不重要,因为所有其他主题都绘制了一个简单的矩形组合框,没有留下角落像素供背景显示。

无论逻辑如何,解决方案仍然是处理 WM_CTLCOLORSTATIC 消息并返回您希望组合框用于绘制其背景的画笔。

此处的注意事项与上面讨论的对话框相同。如果您的窗口使用纯色背景或系统颜色,则您无家可归。只需将句柄返回到您设置为窗口类的背景画笔的同一画笔。如果您想使用渐变,您需要想办法以 GDI 画笔的形式表示该渐变。

WNDCLASSEX wcex;
wcex.cbSize        = sizeof(WNDCLASSEX);
wcex.style         = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc   = WndProc;
wcex.cbClsExtra    = 0;
wcex.cbWndExtra    = 0;
wcex.hInstance     = hInstance;
wcex.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_APPLICATION));
wcex.hIconSm       = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_APPLICATION_SMALL));
wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_3DDKSHADOW + 1);  // background brush
wcex.lpszMenuName  = NULL;
wcex.lpszClassName = TEXT("My Colored Window Class");
RegisterClassEx(&wcex);
case WM_CTLCOLORSTATIC:
{
  // NOTE: No leak here because we're using a system brush in this example.
  return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_3DDKSHADOW)); // background brush
}

                    

【讨论】:

    【解决方案2】:

    如何去除组合框中的白角?

    我不知道是否有更正式的方法来摆脱它,但一种选择是使用CreateRoundRectRgn() 创建一个带圆角的Region,然后使用@987654323 将其应用于ComboBox @。这将掩盖角落。

    【讨论】:

      猜你喜欢
      • 2019-02-22
      • 2012-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多