Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Flicker free resizable custom control

0.00/5 (No votes)
31 Oct 2005 1  
This dynamically reziable control does not flicker. The article describes the problem and the technique used to solve the flickering.

Sample Image

Introduction

I had read every article on flicker-free drawing, yet I could not make it work for my resizable custom control. There are a couple of known issues, but there is also a rarely mentioned subtlety involved. In this article, I will discuss all the issues involved and how to approach them.

The sample included in this article is an implementation of an elliptical button control. The drawing is done using GDI+. The round button changes outer color as the mouse cursor rolls over. The inner color changes as the mouse is clicked on the button. The control stretches and shrinks as the dialog box is dragged.

Background

Many articles on the subject suggest to:

  1. use double buffering
  2. override OnEraseBkgnd and return TRUE.

Well, I implemented both and it still flickered. The problem most visibly occurred during resizing, when the whole dialog was repainted. The issue was that the dialog box would erase my custom control as it erased its own background. Therefore, every time I resized the dialog, I would get a flicker due to the dialog box painting grey over my control before the control repainted itself.

Using the code

The clipping solution: tell the dialog to erase everything but the region occupied by the round control. The easiest way to accomplish this is to set the Windows style to WS_CLIPCHILDREN. You can do this programmatically in "OnInitDialog" method (or you can set this dialog property in Visual Studio's design view):

BOOL CNoFlickerDlg::OnInitDialog()
{
    ....

    // Tell the dialog NOT to erase children controls

    ModifyStyle(0, WS_CLIPCHILDREN);

    return TRUE;
    // return TRUE  unless you set the focus to a control

}

Since my control has a custom (round) region, it must be re-computed each time the control changes shape or size. In order for this to work correctly, the best place to call the ReCalculateDimensions function is from the dialog's OnEraseBkgnd, like so:

BOOL CNoFlickerDlg::OnEraseBkgnd(CDC* pDC)
{
    m_cButtonControl.ReCalculateDimensions();

    return CDialog::OnEraseBkgnd(pDC);
}

The reason you have to make the call here is important. The dialog will exclude the control region from its surface (that is being erased) based on what the control region is set to be. Therefore, by calling it right before CDialog::OnEraseBkgnd, you ensure that the control region is up to date right before it is clipped.

Double Buffering: Since all drawing is done in GDI+, I used a CachedBitmap class to help me with double buffering. The code comes down to the following few lines:

void CButtonControl::OnPaint()
{
    // device context for painting

    CPaintDC dc(this);

    Graphics graphics(dc.m_hDC);

    // Only redraw the control if the buffer is dirty

    if (m_bDirtyBuffer)
    {
        DrawButton(graphics);
        m_bDirtyBuffer = FALSE;
    }

    graphics.DrawCachedBitmap(m_pCachedBitmap, 0, 0);
}

As you can tell from the code above, the actual drawing is only done when m_bDirtyBuffer flag is set to true. Every other time, the painting is done using a cached bitmap. You set the flag to true whenever the control is resized (i.e., in OnSize method).

Finally, make sure that the control itself has OnEraseBkgnd method overridden and returns TRUE. This will make your controls flicker free!

Points of Interest

Download the demo and drag it by the corner. See the button change shape without any flicker.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here