I'm trying to make a simple paint program in C#, but it keeps flickering when I'm drawing, like I need some kind of double buffering, but I don't know how to do it.
I am drawing on a Panel
and I'm using a Bitmap
to store the graphics.
Here's my code:
public partial class Form1 : Form
{
private Bitmap drawing;
private bool leftDown = false;
private int prevX;
private int prevY;
private int currentX;
private int currentY;
public Form1()
{
InitializeComponent();
drawing = new Bitmap(panel1.Width, panel1.Height);
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
leftDown = true;
prevX = e.X;
prevY = e.Y;
}
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (leftDown)
{
Graphics g = Graphics.FromImage(drawing);
currentX = e.X;
currentY = e.Y;
g.DrawLine(new Pen(Color.Black), new Point(currentX, currentY), new Point(prevX, prevY));
panel1.Invalidate();
prevX = currentX;
prevY = currentY;
g.Dispose();
}
}
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
leftDown = false;
}
private void panel1_MouseLeave(object sender, EventArgs e)
{
leftDown = false;
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImageUnscaled(drawing, 0, 0);
}
}
You not only should turn DoubleBuffered
to true
, but also use PictureBox
instead of Panel
and draw on it. This should solve your issue :).
You need to subclass Panel to do this, because you need to override certain things. A Panel
like this should work:
class DoubleBufferedPanel : Panel {
public DoubleBufferedPanel() : base() {
this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.DoubleBuffered |
ControlStyles.Opaque |
ControlStyles.OptimizedDoubleBuffer, true);
}
public override void OnPaint(PaintEventArgs e) {
// Do your painting *here* instead, and don't call the base method.
}
// Override OnMouseMove, etc. here as well.
}
However, you don't need the functionality Panel
adds to Control
at all, that is for it to be functioning as a container. So, in fact, you should be inheriting from Control
unless you need subcontrols.
Another improvement might be to only Invalidate
with the Rectangle
that changed. This will repaint one area and reduce drawing time. You can also pass a srcRect
to Graphics.DrawImage
, that srcRect
being calculated from e.ClipRectangle
, for even better performance if subclassing Panel
doesn't work for you.
When painting pixel maps in the Paint event, the flickering is often caused by Windows forms because it first draws the background and then the pixel map. So the flicker is the background that becomes visible for a fraction of a second.
You can set the Opaque
style in the panel's ControlStyle
property. That will turn of the background drawing because Windows Forms now assumes that your code will completely draw the contents of the panel.
Normally, simply adding this.DoubleBuffered = true;
in your code should do the trick.
If it does not, add this code to your form :
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
You can also change it using Reflection, but there is no advantage, neither readability or speed.