Coder
Coder

Reputation: 3715

Correct way to do transparent buttons in WINAPI

How do you make the button with overrided WM_PAINT transparent. So that you could see the form through it in all places except where something is drawn. At the same time avoiding the flicker.

I saw examples with SetBkMode(HDC, TRANSPARENT), using NULL_BRUSH, examples with CompatibleDC and BitBlts, but I'm not quite sure which is the correct way to go, and also, how it behaves when WM_CLIPCHILDREN is set on the parent window. Most of the experiments I did had some weird behavior as well. Can't find a good documentation on WM_ERASEBKGND/WM_CTLCOLOR/WM_PAINT/WS_EX_COMPOSITED/WS_CLIPCHILDREN/etc internal relations on MSDN at all.

Does anyone know where I could read about this topic with all the gotchas associated?

Upvotes: 3

Views: 6106

Answers (1)

Chris Becke
Chris Becke

Reputation: 36016

mmm, I've never found anything close to a authoritative document on this topic.

This just serves as my rather random memory dump trying to get controls to "play nice", when animated, on a window that was either skinned (normal non client area with a bitmap background), layered (to get a window with custom non client edges with drop shadow effects) or with extended Aero Glass (via the DwmExtendFrameIntoClient) effects.

  • SetBKMode(... TRANSPARENT) just ensures that text rendering does not fill in the background of the text with the current bk color.
  • WS_EX_COMPOSITED causes windows to paint the parent and all child windows to a back buffer when the parent is invalidated, and then paint the back buffer. This is flicker free, but NT 6.0 introduced the desktop window manager which does not honor WS_EX_COMPOSITED.
  • WS_CLIPCHILDREN prevents the child windows and the parent window painting the same area twice. But is contra indicated if you need to use group boxes, or tab controls.
  • WS_CLIPSIBLINGs could be useful if child windows overlap and cause flicker. again this style is useless if you need to use group boxes or tab controls.
  • The other problem with WS_CLIPCHILDREN is you can't paint a background in the parent widnows window proc and rely on a NULL brush to reveal the skin. You can return a brush from WM_CTLCOLORxxx messages to coerce some of the standard controls to paint their background with your skin bitmap.
  • WS_EX_LAYERED is another style that makes windows buffer the painting of your parent window. But the layered windows painter does not paint child windows at all, so you need to manually paint the child windows by sending WM_PRINTCLIENT messages. Not all controls support this message.
  • WPF gets around the lack of back buffered painting and alpha support by not creating actual child windows at all for its buttons.

Final take on the situation:

With a little work you can get a skin behind most standard controls easily. WS_CLIPCHILDREN and no background painting on the parent will mimimize flicker. Handle WM_CTLCOLORxxx to fill the background on the controls.

If you are using Group Boxes or TabControls to frame other controls you absolutely must get the Z-order correct if using WS_CLIPSIBLINGS.

By sending controls WM_PRINTCLIENT messages, and some subclassing, you can get standard controls to paint onto a DIBSection, which you can then manually (or use DWM worker functions) repair the alpha channel of, and then paint onto a layered window, or a window with extended aero glass. This is even flicker free, but controls that don't support WM_PRINTCLIENT, or frequently update themselves outside of WM_PAINT, will not display correctly.

Upvotes: 7

Related Questions