9
votes

I'm working on a simple vector drawing app in C#/.Net. The drawing is done in a panel, but I'm not using the OnPaint() event for all of it - in fact the OnPaint() even just calls another method which actually draws everything in the document.

I tried to add double buffering, but when I set DoubleBuffered to true, the flicker issue is even worse. Why is this? If I want to double buffer the control, do I absolutely have to do all my drawing in the OnPaint() event, with the supplied Graphics object, instead of using Panel.CreateGraphics() and then drawing to that?

EDIT: This is the basic code I am using.

private void doc_Paint(object sender, PaintEventArgs e)
{
    g = doc.CreateGraphics();
    Render(ScaleFactor, Offset);
}    

private void Render(float ScaleFactor, PointF offset)
{
    foreach (Line X in Document.Lines) { DrawLine(X.PointA, X.PointB, X.Color, X.LineWidth); }
}
private void DrawLine(PointF A, PointF B, Color Color, float Width)
{
    Pen p = new Pen(Color, Width);
    PointF PA = new PointF(((A.X + Offset.X) * ScaleFactor), ((A.Y + Offset.Y) * ScaleFactor));
    PointF PB = new PointF(((B.X + Offset.X) * ScaleFactor), ((B.Y + Offset.Y) * ScaleFactor));
    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
    g.DrawLine(p, PA, PB);
}

The general idea is that the two variables, ScaleFactor and Offset, refer to the zoom level and pan level in the UI. g is a Graphics object.

2
Can you post the code in your panel's paint event handler and whatever functions it calls?Brandi
Yeah, instead of having a global Graphics object, have a global bitmap. Also, when you have PaintEventArgs e, you don't have to say "doc.CreateGraphics();" you can just say "e.Graphics;"Brandi
If you don't mind an ummanaged solution look here: stackoverflow.com/questions/2682025/… (the accepted answer) That often worked for me.Jürgen Steinblock

2 Answers

29
votes
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.

1
votes

Personally I don't bother with the DoubleBuffered setting. I just draw everything to a bitmap and then in the paint event draw that bitmap on the screen.

Bitmap BackBuffer;

private void MainFormSplitContainerPanel1Paint(object sender, PaintEventArgs e)
{
    e.Graphics.Clear(MainFormSplitContainer.Panel1.BackColor);
    if (BackBuffer != null)
        e.Graphics.DrawImage(BackBuffer, positionX, positionY, SizeX, SizeY);
}