Getting rid of flickering in .NET CF forms with custom drawn controls

.NET Compact Framework doesnt provide builtin double buffering for forms. In case you have multiple custom drawn user controls on a form, loading will mostly show flickr. Overriding OnPaint and drawing the form and its child controls to an offscreen bitmap before putting it out to actual display is expected to solve this. But, once a child control is added to another container control, overriding OnPaint of the parent container control doesnt allow you to paint the child as the child’s client rect is clipped out from the graphics object passed to parent’s OnPaint.
The solution to this problem is to avoid adding the custom user control to the forms. Instead, maintain your own list of such controls. Form’s constructor should make these changes.

//this.Controls.Add(customControl); //don't do this
control_list.Add(customControl); //control_list is Forms own custom list for it's child controls.

When we do that, GWES doesn’t know of these controls as child to our form and hence allows us to paint anywhere we like. We can now paint all child controls (by calling their respective OnPaint) to an offscreen bitmap before blting it to actual display. Heres a sample code which does something like this.

protected override void OnPaint(PaintEventArgs e)
{
using (Bitmap doubleBuffer = new Bitmap(this.Width, this.Height))
{
using (Graphics gxOff = Graphics.FromImage(doubleBuffer))
{
using (Brush backBrush = new SolidBrush(this.BackColor))
{
gxOff.FillRectangle(backBrush, this.ClientRectangle);
}

foreach (CustomControl ctl in control_list) //control_list is list of child controls maintained by Form
{
Rectangle rc = new Rectangle(ctl.Left, ctl.Top, ctl.Width, ctl.Height);
if (e.ClipRectangle.IntersectsWith(rc)) //update only those child control which fall within the invalidated region
{
ctl.OnPaint(new PaintEventArgs(gxOff, ctl.ClientRectangle), ctl.Left, ctl.Top);
}
}

e.Graphics.DrawImage(doubleBuffer, 0, 0);
}
}
}

This scheme, however, also requires the form to manage and delegate other GWES events (keypress, clicks, etc) to respective child control. Following code snippet shows that for MouseDown

protected override void OnMouseDown(MouseEventArgs e)
{
foreach (CustomControl ctl in control_list)
{
Rectangle rc = new Rectangle(ctl.Left, ctl.Top, ctl.Width, ctl.Height);
if (rc.Contains(new Point(e.X, e.Y)))
{
ctl.OnMouseDown(e);
this.Invalidate(rc);
break;
}
}
}

I know its a bit of pain but in case you have simple custom controls like buttons, picturebox, very few events need to be overridden and its worth for the flickr free smooth rendering of your forms.

SureMDM: Mobile Device Management for Starters and Experts

3 Comments

  1. wei on Saturday June 11th, 2011 at 11:35 AM

    Thanks a lot pal!

  2. Marek on Tuesday June 7th, 2011 at 08:00 PM

    1. Which .NET CF namespace contains CustomControl type? (foreach (CustomControl ctl in control_list))
    2. How can you call ctl.OnPaint? Isn’t OnPaint a protected method?

    I have a custom control where I am overriding OnPaint method (protected override void OnPaint(PaintEventArgs e)) and then I have a User Control that contains 2 custom controls. I am trying to display the User Control at once, rather then displaying one custom control and then the other. But the OnPaint method cannot be called on the custom controls.

    • Prakash on Sunday June 12th, 2011 at 12:35 PM

      Hi
      1) CustomControl is an illustrative name for you custom control class. It doesnt come with .NET CF.
      2) Yes .NET CF Control class’s OnPaint() is protected. So in case your custom control is being derived from Control class, you can have a public MyOnPaint() method in your Custom control class from where you can call its own OnPaint().

Leave a Comment