c++ - 渐变父窗口上的背景组合框?

标签 c++ windows user-interface winapi combobox

我有带渐变背景的窗口。组合框具有自己的背景画笔。
如何删除组合框中的白角?如何更改画笔或其他方式。
在图片的白色角上标有红色框。

combobox with white corner

我将组合框创建为:

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

第二种方式。

在父级WndProc中,在WM_ERASEBKGND消息中绘制背景,则角不会。
case WM_ERASEBKGND:
    m_hdc = (HDC)wParam;

    // draw background.

    return TRUE;
break;

两种方法的结果:

combobox without white corner

最佳答案

对于对话框,处理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);
}

这是更改对话框背景颜色的标准记录方法,它也解决了组合框的问题。显然,无论出于何种原因,组合框控件也使用此笔刷绘制其背景。我想他们在绘画时会向 parent 发送WM_CTLCOLORDLG消息。
当然,这将限制您使用GDI画笔的图形功能。您可以绘制所需的任何系统色或纯色,甚至可以使用图案填充或图案/位图画笔,但是没有创建渐变画笔的简单方法。 (GDI +有一个,但没有GDI。)通常没关系-您只需在GradientFill(甚至WM_PAINT)消息处理程序中调用WM_ERASEBKGND函数即可。这对于对话框的背景来说效果很好,但是组合框仍然使用WM_CTLCOLORDLG返回的画笔绘制背景,因此它的角落上仍然有4个点用COLOR_3DFACE绘制(这是默认对话框过程返回的画笔)。

NULL_BRUSH返回空画笔(HOLLOW_BRUSH / WM_CTLCOLORDLG)也不起作用。它会稍微改变外观,以至于右上角和左下角像素现在都填充了类似于COLOR_3DSKSHADOW的内容,但仍然可见地填充了实际背景渐变以外的其他颜色。

因此,如果您真的希望它看起来不错,则只剩下一个选择:将手柄返回给GDI画笔。当然,它必须与用于绘制对话框背景的画笔相同。
如果要进行渐变填充,我能想到的唯一解决方案是使用图案/位图画笔,其中位图(DDB或DIB)就是您的渐变。效果不是很好,但是至少Windows 9x将我们限制为8×8模式的日子已经过去了。也许比我更有创造力的人可以使用此信息来考虑更好的解决方法?

对于其他窗口,请处理WM_CTLCOLORSTATIC并为组合框返回背景画笔
所有这些都用于对话框。但是,如果您在标准窗口(即对话框以外的其他窗口)中显示组合框,该怎么办?在这种情况下,永远不会发送WM_CTLCOLORDLG消息。
而是,组合框将WM_CTLCOLORSTATIC消息发送到其父窗口,然后使用响应该消息而返回的画笔句柄绘制其背景。
我知道这很奇怪。我只是通过进行实证检验而偶然发现了这一点,但我不确定是什么原因。如果我不得不猜测,我会说CBS_DROPDOWNLIST样式使组合框不可编辑(即,由于没有Edit控件,它不是真正的组合框),因此它使用WM_CTLCOLOREDIT而不是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
}

关于c++ - 渐变父窗口上的背景组合框?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37640434/

相关文章:

c# - 从后台线程更新时 UI 稍微卡住

c++ - 如何在C++中正确导航目录路径

c - 有什么方法可以扰乱 Windows 中的 TCP 堆栈吗?

java - 按下 JButton 时重置游戏的 ActionListener

windows - Windows 记事本如何解释字符?

.NET 窗口捕获

java - Android:在回收 View 中获取用户在屏幕上的确切位置

c++ - 为什么我的代码需要 <iostream>?

c++ - 在 #ifdef 中使用 OR

c++ - fortran代码调用c代码时出错