While working on a project recently, I happened to use the WTL
CDialogResize mix-in class, along with a dialog that had groupboxes on it. Whenever I resized the dialog, I noticed that the groupboxes exhibited an annoying flicker that really detracted from the overall smooth affect. I poked around CodeProject and Usenet for a solution but could not find anything, so I decided to solve the problem myself. For such an apparently trivial problem, the solution was quite complex, but the end result is a smooth flicker free resize.
Using the code
The problem with groupboxes is that they are the only (common) control which can contain other controls. For that reason, the center region is not painted by the control itself, and nor are two regions on the top edge of the control.
The groupbox depends on the parent to fill these in with the default background colour. When you use a resizing dialog,
CDialogResize forces the parent window to have the
WS_CLIPCHILDREN style. What this does is exclude all child controls from the parents painting region so that the background is not painted, and flicker is reduced. In most cases this works as the child control paints over its entire owned rectangle, but in the case of the groupbox, this causes problems. The usual way to fix this is to set the
WS_EX_TRANSPARENT extended style for groupboxes, which prevents the
WS_CLIPCHILDREN styled parent window from excluding the groupbox's background region, allowing the background to paint. While this does work, it creates annoying flicker on resize.
My solution was to render groupboxes myself during background painting, and to use a memory dc to reduce flicker. To do this I enumerate all groupboxes on initialization and clear
WS_VISIBLE. This prevents them from rendering themselves. I then build my own valid update region by enumerating all child controls except groupboxes, and fill in that region with the background colour. Last, I enumerate all groupboxes and render them, paying attention to the values of flags
BS_FLAT to ensure they render in exactly the same form as the originals. Font metrics is also accounted for, to make sure the groupboxes top offset is correct. On initialization, the code determines if the user is running XP or greater and if so it renders XP style groupboxes (rounded corner, single line). If not XP, it renders the standard square 3D rect style.
For a few bonuses, I threw it the ability to render XP style groupboxes on W2000/9x (or W2000/9x style on XP) by calling
SetXPStyleGroupBoxes(). I also added the ability to set a Minimum and Maximum resize limit. Minimum did exist already, but would default to the current design size of the dialog resource. This is probably a good idea, but if you want you can set it smaller using
SetMaxTrackSize() allows the setting of a maximum window size. If you do not call this function then the window may be maximized to full screen as normal.
There are a few issues I did not address. By default, W2000/XP will not show accelerator (e.g.: OK) underlines until the ALT key is pressed. My replacement groupboxes do not do this, they always show the accelerator. Also, I only coded for normal text mode groupboxes. I have never used them but there are apparently icon and bitmap groupboxes also. Those are not dealt with, so they should render, but flicker as usual. Personally I've never used those styles in 15 years. Usage of a MemDC slightly slows resizing, as a MemDC is getting created and destroyed on every
WM_ERASEBKGND. This is more noticeable the bigger the window client area gets, but on my pc is not bothersome. For the XP groupboxes, I have no idea what standard syscolor they are rendered in. I used
COLOR_3DSHADOW but it is not exactly right, but close enough.
Usage is very similar to the standard
CDialogResize mix-in class, just replace it with
CNoFlickerDialogResize. Thats the only difference. Make sure and include
nfresize.h somewhere in the project, and to have atlgdix.h (mentioned below) somewhere in the include path.
class CMainDlg :
From there, you do everything as you would when using the standard
CDialogResize, set up a resize map and call
OnInitDialog. Michael Dunn has a very good tutorial article on
CDialogResize here, if you want to learn usage. Also in
OnInitDialog you can call using
SetMaxTrackSize() if required.
There is no practical use except for demonstration, but the function
DisableFlicker(bool) if set to false will return painting to normal windows default, and the flicker will resurface.
The demo zip
contains a silly app to demonstrate the general effect of the flicker reduction. You can turn anti-flicker on and off, and show groupboxes either as XP style or old-school. Fonts can be changed to show proper font handling. This dialog has a maximum upper size of 800 x 600, so that is also demonstrated.
I used the CMemDC class by Bjarke Viksoe in this project, so I included atlgdix.h in the zip. Kudos to Bjarke for making that excellent add in. Check out his website for more excellent stuff.