Win32 transparent controls on all versions ofWindows

0 votes
asked Dec 13, 2010 by jwood

I'm working on a Win32 GUI app using plain Win32 API (no MFC or .NET). The issue I'm having is making controls appear transparent. I've come up with a method which works for most things, in Windows Vista+ I do this in the WndProc:

case WM_CTLCOLORSTATIC:
{
    SetBkMode((HDC)wParam, TRANSPARENT);
    return (INT_PTR)::GetStockObject(NULL_PEN);
}
break;

In Windows XP, I do this in the WndProc:

case WM_CTLCOLORSTATIC:
{
    HBRUSH hbr = (HBRUSH)DefWindowProc(hDlg, message, wParam, lParam);
    ::DeleteObject(hbr);
    SetBkMode((HDC)wParam, TRANSPARENT);
    return (LRESULT)(HBRUSH)(COLOR_WINDOW);
}

Now this works for most of the controls, however I get a transparent background on the label on the top of a group box control which draws the group box line through the text. I started working towards a case for just group boxes but I'm sure that this is a problem which must have been solved before and I don't want to go re-inventing the wheel.

Is there a tried and tested method for making controls appear transparent?

Thanks, J

1 Answer

0 votes
answered Dec 15, 2010 by chris-becke

To achieve transparent controls you are going to have to be aware that:

  • You can't really. The standard windows controls just don't support "transparent" painting.
  • Even when you get it right, the dialog is going to flicker badly if you resize it.
  • The 'hacks' to get transparent painting of controls working tend to be different if theming is on or off, and change between windows versions.

Usually the goal of making controls "transparent" is so that a bitmap skin under the controls shows through. The way to achieve this kind of transparency is to create a bitmap for the background of the control. Then use CreatePatternBrush from the bitmap.

This chunk of DialogProc code implements the simplest skinning method possible and will then take care of painting both the background of the dialog, and most of the controls that support this form of painting:

  // _hwnd is the dialogs handle
  // _hbrSkin is a pattern brush handle
  HWND hwndCtl;
  POINT pt;
  HDC hdc;
case WM_CTLCOLORDLG:
  return (INT_PTR)_hbrSkin;
case WM_CTLCOLORSTATIC:
case WM_CTLCOLORBTN:
  hdc = (HDC)wParam;
  SetBkMode(hdc,TRANSPARENT); // Ensure that "static" text doesn't use a solid fill
  pt.x = 0; pt.y = 0;
  MapWindowPoints(hwndCtl,_hwnd,&pt,1);
  SetBrushOrgEx(hdc,-pt.x,-pt.y,NULL);
  return (INT_PTR)_hbrSkin;

Controls that overlap will draw incorrectly as one will paint its "transparent" background over the other. You can reduce the flicker by:

  • Not allowing the dialog to be resized.
  • setting the WS_EX_COMPOSITED style on the dialog, but as the Windows NT 6 DWM doesn't support it, its essentially useless from Vista on.
  • Setting the WS_CLIPCHILDREN style on the dialog & or WS_CLIPSIBLINGS - these styles prevent the use of group boxes and tab controls as they rely on controls overlapping.
  • subclassing all the controls, using the WM_PRINTCLIENT message to paint them to a backbuffer, then blitting the prepared backbuffer in one pass. Hard work and not all controls support WM_PRINTCLIENT.
Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...