3

I have a VS2008 C++ application for Windows XP SP3 developed using WTL 8.1. My application contains a tab control that flickers when the application border is resized.

My window hierarchy looks like this:

CFrameWindowImpl   CMainFrm
|-CSplitterWindow  Splitter
  |-CTabView       Configuration Tabs
  | |-CDialogImpl  Configuration View 1
  | |-CDialogImpl  Configuration View 2
  | |-CDialogImpl  Configuration View 3
  |-CDialogImpl    Control View

The solution I'm trying is to make the CFrameWindowImpl derived class use the WS_EX_COMPOSITED style and all windows beneath it use the WS_EX_TRANSPARENT style. Unfortunately, this makes the tab control buttons show as an empty black bar and the controls of any Configuration View to not show at all.

If I remove the WS_EX_COMPOSITED and WS_EX_TRANSPARENT styles, the form displays properly, but the CTabView and everything beneath it flickers horribly when resized.

What do I need to change to eliminate the flicker and draw the controls properly?

Thanks, PaulH


Edit: Got it working. I removed all the WS_EX_TRANSPARENT styles per Mark Ransom's suggestion. I put the WS_EX_COMPOSITED style on only the CTabCtrl (contained within the CTabView). Other controls get double-buffering as needed through WTL::CDoubleBufferImpl<>.

PaulH
  • 7,759
  • 8
  • 66
  • 143
  • 3
    WS_EX_TRANSPARENT probably doesn't do what you think it does. All it does is force the child windows to paint after the parent, which is at odds with what WS_EX_COMPOSITED is trying to accomplish. – Mark Ransom Nov 15 '10 at 21:07

3 Answers3

5

A window flickers because it gets erased before it's drawn. To eliminate this you need to disable erasing of the window entirely and use double buffering - draw the window contents into a bitmap, then copy the bitmap to the window. Because the bitmap contains the entire contents including the background, there's no need to erase anymore.

It looks like WS_EX_COMPOSITED will handle the double buffering automatically, but you still probably need to use a NULL background brush and/or handle the WM_ERASEBKGND message.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 1
    According to the MSDN docs, `WS_EX_COMPOSITED` implements double buffering: http://msdn.microsoft.com/en-us/library/ms632680%28VS.85%29.aspx – PaulH Nov 15 '10 at 20:09
  • @PaulH, I was already coming to the same conclusion myself. I made an update that might help. – Mark Ransom Nov 15 '10 at 20:10
  • When I use the `WS_EX_COMPOSITED` style, the flickering is gone. The problem is that some things don't get drawn at all anymore (like the tab control buttons turning in to a solid black bar across my window). Are you saying handling `WM_ERASEBKGND` as you describe will fix that? – PaulH Nov 15 '10 at 20:14
  • Handling WM_ERASEBKGND to do nothing in the CFrameWindowImpl had no effect on the flickering. – PaulH Nov 15 '10 at 20:34
  • @PaulH, it looks like Chris Becke has the best explanation for the black bars. I didn't expect WM_ERASEBKGND to affect that at all, only the flickering. – Mark Ransom Nov 15 '10 at 21:09
4

Whats not mentioned in MSDN is that the Desktop Window Manager - the component that hooks window painting on Windows Vista and 7 to perform the desktop composition necessary to get the aero glass effect - does NOT implement WS_EX_COMPOSITED.

Which means all the work you put into getting this style to work on XP, is doomed to become irrelevent on Vista or later.

The other problem with WS_EX_COMPOSITED - and why it was an optional style and not a default on XP: The double buffering only picks up painting performed during the BeginPaint / EndPaint block of the parent window. Lots of, even standard controls, perform painting outside of their WM_PAINT handlers, and as a result the backbuffer gets only partially painted.

Sadly, the result is, the only way to "eliminate" flicker in native API apps is to try to minimize it: WS_CLIPCHILDREN and WS_CLIPSIBLINGS can help if you dont have overlapping controls - to ensure that each control's area is painted only once. And ensure that the main dialog does not perform any flood filling in WM_ERASEBKGND

Chris Becke
  • 34,244
  • 12
  • 79
  • 148
  • I have added WS_CLIPSIBLINGS and WS_CLIPCHILDREN to all of the windows listed in my OP and handled WM_ERASEBKGND to do nothing in CMainFrm. This had no effect on the flickering. – PaulH Nov 15 '10 at 20:36
0

It is not, in my experience, possible to use double-buffering for anything that contains child controls (unless they all fully support WM_PRINT, which most do not).

Mordachai
  • 9,412
  • 6
  • 60
  • 112