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
Easiest Way to Read from a Url into a String in .Net
How to Catch a Specific SQLexception Error
How to Get the Child Windows of a Window Given Its Hwnd
Cannot Access a Disposed Object in ASP.NET Core When Injecting Dbcontext
How to Bind Datatable to Datagrid
Zoom and Translate an Image from the Mouse Location
Validation Error Style in Wpf, Similar to Silverlight
Large Switch Statements: Bad Oop
What Is an MVChtmlstring and When Should I Use It
Value Cannot Be Null. Parameter Name: Source
Getting Serial Port Information
How to Render a Razor View to a String in ASP.NET MVC 3
What Does It Mean for a Property to Be [Required] and Nullable
Databindings Don't Seem to Refresh
Best Practice to Make a Multi Language Application in C#/Winforms