Double Buffering When Not Drawing in Onpaint(): Why Doesn't It Work

Double Buffering when not drawing in OnPaint(): why doesn't it work?

g = doc.CreateGraphics();

That's the mistake. Double-buffering can only work if you draw into the buffer. The one that e.Graphics references. Fix:

g = e.Graphics;

Beware that Panel doesn't have double-buffering turned on by default. You'll need to derive your own. Paste this into a new class:

using System;
using System.Windows.Forms;

class BufferedPanel : Panel {
public BufferedPanel() {
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
}

Compile. Drop it from the top of the toolbox.

A problem with overriding OnPaint when DoubleBuffered set to true

In your PaintSelection, you should not create a new Graphics object, because that object will draw to the front buffer, which is then promptly overdrawn by the contents of the back buffer.

Paint to the Graphics passed in the PaintEventArgs instead:

protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
PaintSelection(pe.Graphics);
}

private void PaintSelection(Graphics graphics)
{
if (isSelected)
{
graphics.DrawRectangle(SelectionPen, DisplayRectangle.Left, DisplayRectangle.Top, DisplayRectangle.Width - 1, DisplayRectangle.Height - 1);
}
}

C# Double Buffered not painting

Remove the using statement. You are disposing of the Graphics object before it is used to draw to the screen.

As mentioned, you should also remove the Invalidate() call from the paint method.

public void panelMap_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
g.DrawImage(mapController.GetCurrentMap(), 0, 0, panelMap.Width, panelMap.Height);
}

Despite double buffering, the ticker still flickers

Turning Hans Passant's comment into an answer so that I can accept it:

This effect is not called flicker, it is called tearing. You see part
of the old bitmap and part of the new bitmap. Which becomes very
noticeable on moving objects, they appear jittery. Not fixable in
Winforms, google "vertical blanking interval". – Hans Passant

Efficient use of OnPaint

Create a Bitmap object, and draw to that.

In your Paint handler, just blit the Bitmap to the screen.

That will allow you decouple changing the scale, from re-rendering the data.

Paint Event e.Graphics.DrawImage doesn't seem to follow double buffering

As already mentioned in the comments - to enable Double Buffering of the panel you need to SetStyle() within the panel constructor, not in the form load event. To do so, you have to create your own panel. Bellow the sample code of custom panel class.

public partial class CustomPanel : UserControl
{
public CustomPanel()
{
InitializeComponent();

// Add Double Buffering
SetStyle(ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
}
}

Why should I do manual double buffering?

In WFA, double-buffering slows performance without completely eliminating flicker in custom graphics areas. For built-in GUI elements, like if you create a game built out of ImageButtons and Labels, the built-in double-buffered mode is great at hiding the redrawing of the control tree. However, there are a couple major problems with using it for a custom drawing area:

  • The draw buffer created when you just set up the application to draw double-buffered is used to draw the whole window and all child controls, not just your custom drawing area, so you add the overhead of redrawing every GUI element on the back buffer before page-flipping.
  • If anything invalidates the control, the Paint method is called. You may not be finished drawing when that happens and so you'll get an incomplete image shown to the user (not good in real-time graphics).

By keeping the basic window GUI single-buffered, but creating an area on which you control the buffering, both of these problems are minimized.

Double-buffering methods can be as simple as creating a Bitmap object as a back-buffer and drawing it to the draw area when you're good and ready, or setting up a seperate BufferedGraphicsContext to manage buffering of your custom draw area.

.NET CF double-buffering when painting over base control

Double-buffering in the CF is a manual process, so I would assume that your base class holds an Image or Bitmap onto which it is drawing? It depends on exactly how you're doing your painting but you can either make that Image protected or you can do something slightly more complex like this:

protected virtual void OnPaint(graphics bufferGraphics) { } 

void OnPaint(PaintEventArgs pe)
{
var buffer = new Bitmap(this.Width, this.Height);
var bufferGraphics = Graphics.FromImage(buffer);

// do base painting here
bufferGraphics.DrawString(....);
// etc.

// let any child paint into the buffer
OnPaint(bufferGraphics);

// paint the buffer to the screen
pe.Graphics.DrawImage(buffer, 0, 0);
}

Then in your child, simply override the new OnPaint and do what you want with the incoming Graphics object, which paints onto the buffer, not the screen.

If you want the child to be able to completely override base painting, just move the base painting logic into the virtual method.

Panel DoubleBuffered property stopped the drawing, is invisible

Try using ControlStyles.OptimizedDoubleBuffered instead. It's faster and usually works better. Make sure ControlStyles.AllPaintingInWmPaint and ControlStyles.UserPaint are also enabled.

Now, OnPaint() should be the only stuff that draw to the window and this method must only be called from invalidation or by using Refresh(); you must never call OnPaint() yourself. Do not dispose the Graphics object. If you fail any of those conditions, flickering and various other drawing bugs might occur.

class MyControl : UserControl
{
public MyControl()
{
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}

protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.Clear(Color.Red);
}

void RandomEventThatRequiresRefresh(object sender, EventArgs e)
{
Refresh();
}
}


Related Topics



Leave a reply



Submit