Introduction
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_LEFT,BS_CENTER,BS_RIGHT
and 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 SetMinTrackSize()
. 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.
#include "nfresize.h"
class CMainDlg :
public CDialogImpl<CMainDlg>,
public CNoFlickerDialogResize<CMainDlg>
{
From there, you do everything as you would when using the standard CDialogResize
, set up a resize map and call DlgResize_Init()
in 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 SetMinTrackSize()
and/or 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.
Demo App
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.