梯度父窗口上的背景组合框? [英] Background combobox on gradient parent window?

查看:193
本文介绍了梯度父窗口上的背景组合框?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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





我创建combobox为:

  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;



=== SOLVED。 WORK VERSION ===



第一种方式。



在父WndProc中:

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

在我的课程:

  // 
// CListBox :: GetHbrush()。
//
//获取刷。
//
HBRUSH CListBox :: GetHbrush()
{
if(!m_hBrush)
{
m_hBrush = CreateTransparentBackgroundBrush(m_hParent,m_hWnd);
}
return m_hBrush;
}

创建透明背景:

  // 
// CListBox :: CreateTransparentBackgroundBrush()。
//
//为元素创建透明背景。
//
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(父,& 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;

//绘制背景。

return TRUE;
break;



两种方法的结果:



解决方案

对于对话框,请处理 WM_CTLCOLORDLG 并为组合框返回一个背景刷



如果你在一个对话框中显示这个组合框,实际上是处理 WM_CTLCOLORDLG 消息。为了响应这个消息,你返回一个画笔的句柄,对话框将使用它来绘制其背景。

  case WM_CTLCOLORDLG :
{
//注意:这段代码是错误的,因为它在每次处理
//消息时创建一个新的画笔对象,它会立即泄漏。它只是为了演示的目的。
//通常,你会创建刷子一次,响应WM_INITDIALOG,
//缓存它,每次返回相同的缓存句柄,最终破坏
//刷子响应WM_NCDESTROY。
HBRUSH hBrush = CreateSolidBrush(RGB(255,120,0));
return reinterpret_cast< INT_PTR>(hBrush);
}

                      



这是改变对话框背景颜色的标准,文档化的方式,它也解决了组合框的问题。显然,无论什么原因,combobox控件使用这个画笔来绘制他们的背景。我想他们在自己画画时向他们发送 WM_CTLCOLORDLG 消息。



当然,到一个GDI画刷的图形功能。你可以绘制任何你想要的系统或纯色,或者甚至使用填充或模式/位图刷,但没有简单的方法来创建渐变刷。 (GDI +有一个但不是GDI。)通常没关系,你只需在 WM_PAINT GradientFill c $ c>(或甚至 WM_ERASEBKGND )消息处理程序。这对于对话框的背景工作正常,但组合框仍然使用 WM_CTLCOLORDLG 返回的画笔绘制其背景,所以它的角落上仍然有四个点,绘制在 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 样式使组合框不可编辑(,它不是一个真正的组合框,是没有编辑控件),所以代替 WM_CTLCOLOREDIT ,它使用 WM_CTLCOLORSTATIC 。禁用的编辑框也发送 WM_CTLCOLORSTATIC ,因此禁用的组合框与正常​​ CBS_SIMPLE 和<$此外,只有在启用Aero主题时,才会显示此 选项(Vista(C)),而不会显示 cBS_DROPDOWN style。



和7)。它不会发生在Windows 10上,或与Luna主题(XP下的视觉样式)或与经典主题。 (我没有在Windows 8或8.1上测试。)不是那么重要,我想,因为所有这些其他主题绘制一个简单的矩形组合框,没有角落像素的背景通过。



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

这里同样适用于上面讨论的对话框。如果您的窗口使用纯色背景或系统颜色,您是无家可归的。只需将句柄返回到您设置为窗口类的背景画刷的同一个画笔。如果你想使用渐变,你需要找出一种以GDI画刷的形式表示渐变的方法。

  WNDCLASSEX wcex; 
wcex.cb Size = 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:
{
//注意:这里没有泄漏,因为我们在这个例子中使用了系统刷。
return reinterpret_cast< LRESULT>(GetSysColorBrush(COLOR_3DDKSHADOW)); // background brush
}

                      


I have window with gradient background. Combobox have own background brush. How can I remove white corner in combobox? How can I change brush or another way. On picture white corner marked by red frame.

I create combobox as:

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

I tried changing background brush with message WM_CTLCOLOREDIT, but no effect:

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

=== SOLVED. WORK VERSION ===

First way.

In parent WndProc:

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

In my class:

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

Create transparent background:

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

Second way.

In parent WndProc draw background in WM_ERASEBKGND message, then the corners will not.

case WM_ERASEBKGND:
    m_hdc = (HDC)wParam;

    // draw background.

    return TRUE;
break;

The result of both methods:

解决方案

For dialog boxes, handle WM_CTLCOLORDLG and return a background brush for the combobox

If you are displaying this combobox in a dialog, the trick is actually to handle the WM_CTLCOLORDLG message in your dialog's window procedure. In response to this message, you return a handle to a brush that the dialog box will use to paint its background.

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

                    

This is the standard, documented way of changing the background color of a dialog box, and it also solves the problem with the combobox. Apparently, for whatever reason, combobox controls also use this brush to paint their background. I suppose they send a WM_CTLCOLORDLG message to their parent when they are painting themselves.

Of course, this limits you to the graphics capabilities of a GDI brush. You can draw any system or solid color that you want, or even use a hatch or pattern/bitmap brush, but there is no simple way of creating a gradient brush. (GDI+ has one, but not GDI.) Normally it wouldn't matter—you'd just call the GradientFill function in your WM_PAINT (or even WM_ERASEBKGND) message handler. That works fine for the dialog's background, but the combobox still draws its background with the brush returned by WM_CTLCOLORDLG, so it still has those 4 dots on its corners drawn in COLOR_3DFACE (which is the brush that the default dialog procedure returns).

                    

Returning a null brush (NULL_BRUSH/HOLLOW_BRUSH) from WM_CTLCOLORDLG doesn't work, either. It changes the appearance slightly, such that the upper-right and lower-left corner pixels are now filled with something that looks like COLOR_3DSKSHADOW, but they are still visibly filled with a color other than the actual background gradient.

                    

So if you really want it to look nice, you are left with only a single option: returning a handle to a GDI brush. And of course, it needs to be the same brush as is used to draw the dialog's background.

If you want a gradient fill, the only solution I can think of is using a pattern/bitmap brush, where the bitmap (DDB or DIB) is your gradient. Not great, but at least the days of Windows 9x limiting us to 8×8 patterns are long gone. Maybe someone more inventive than me can use this information to think of a better workaround?


For other windows, handle WM_CTLCOLORSTATIC and return a background brush for the combobox

All of that for a dialog box. But what about if you are displaying the combobox in a standard window (i.e., something other than a dialog box)? The WM_CTLCOLORDLG message is never sent in this case.

Instead, the combobox sends a WM_CTLCOLORSTATIC message to its parent window, and then uses the brush handle returned in response to that message to paint its background.

This is weird, I know. I only stumbled across it by conducting empirical tests, and I'm not sure quite sure what the rationale was. If I had to guess, I'd say that the CBS_DROPDOWNLIST style makes the combobox non-editable (i.e., it's not a true combobox because there is no Edit control), so instead of WM_CTLCOLOREDIT, it uses WM_CTLCOLORSTATIC. A disabled Edit box sends WM_CTLCOLORSTATIC, too, and so does a disabled combobox with the "normal" CBS_SIMPLE and CBS_DROPDOWN styles.

Weirder still, this only happens when the Aero theme is enabled (Vista and 7). It doesn't happen on Windows 10, or with the Luna theme (Visual Styles under XP), or with the Classic theme. (I didn't test on Windows 8 or 8.1.) Not that it matters, I suppose, since all of those other themes draw a simple rectangular combobox, leaving no corner pixels for the background to show through.

Whatever the logic, the solution remains to handle the WM_CTLCOLORSTATIC message and return the brush you wish the combobox to use to paint its background.

The same considerations apply here as those discussed above for the dialog box. If your window uses a solid-color background or a system color, you are home-free. Simply return a handle to the same brush that you set as the window class's background brush. If you want to use a gradient, you'll need to figure out a way to represent that gradient in the form of a GDI brush.

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
}

                    

这篇关于梯度父窗口上的背景组合框?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆