I have a program that is essentially like a paint application. However, my program has some flickering issues. I have the following line in my code (which should get rid of flickering - but doesn't):
this.SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
my code(minus the super and sub classes for the shapes is as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Paint
{
public partial class Paint : Form
{
private Point startPoint;
private Point endPoint;
private Rectangle rect = new Rectangle();
private Int32 brushThickness = 0;
private Boolean drawSPaint = false;
private List<Shapes> listOfShapes = new List<Shapes>();
private Color currentColor;
private Color currentBoarderColor;
private Boolean IsShapeRectangle = false;
private Boolean IsShapeCircle = false;
private Boolean IsShapeLine = false;
public SPaint()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
currentColor = Color.Red;
currentBoarderColor = Color.DodgerBlue;
IsShapeRectangle = true;
}
private void panelArea_Paint(object sender, PaintEventArgs e)
{
Graphics g = panelArea.CreateGraphics();
if (drawSPaint == true)
{
Pen p = new Pen(Color.Blue);
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
if (IsShapeRectangle == true)
{
g.DrawRectangle(p, rect);
}
else if (IsShapeCircle == true)
{
g.DrawEllipse(p, rect);
}
else if (IsShapeLine == true)
{
g.DrawLine(p, startPoint, endPoint);
}
}
foreach (Shapes shape in listOfShapes)
{
shape.Draw(g);
}
}
private void panelArea_MouseDown(object sender, MouseEventArgs e)
{
startPoint.X = e.X;
startPoint.Y = e.Y;
drawSPaint = true;
}
private void panelArea_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (e.X > startPoint.X)
{
rect.X = startPoint.X;
rect.Width = e.X - startPoint.X;
}
else
{
rect.X = e.X;
rect.Width = startPoint.X - e.X;
}
if (e.Y > startPoint.Y)
{
rect.Y = startPoint.Y;
rect.Height = e.Y - startPoint.Y;
}
else
{
rect.Y = e.Y;
rect.Height = startPoint.Y - e.Y;
}
panelArea.Invalidate();
}
}
private void panelArea_MouseUp(object sender, MouseEventArgs e)
{
endPoint.X = e.X;
endPoint.Y = e.Y;
drawSPaint = false;
if (rect.Width > 0 && rect.Height > 0)
{
if (IsShapeRectangle == true)
{
listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
}
else if (IsShapeCircle == true)
{
listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
}
else if (IsShapeLine == true)
{
listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
}
panelArea.Invalidate();
}
}
private void rectangleToolStripMenuItem_Click(object sender, EventArgs e)
{
IsShapeRectangle = true;
IsShapeCircle = false;
IsShapeLine = false;
}
private void ellipseToolStripMenuItem_Click(object sender, EventArgs e)
{
IsShapeRectangle = false;
IsShapeCircle = true;
IsShapeLine = false;
}
private void lineToolStripMenuItem_Click(object sender, EventArgs e)
{
IsShapeCircle = false;
IsShapeRectangle = false;
IsShapeLine = true;
}
private void ThicknessLevel0_Click(object sender, EventArgs e)
{
brushThickness = 0;
}
private void ThicknessLevel2_Click(object sender, EventArgs e)
{
brushThickness = 2;
}
private void ThicknessLevel4_Click(object sender, EventArgs e)
{
brushThickness = 4;
}
private void ThicknessLevel6_Click(object sender, EventArgs e)
{
brushThickness = 6;
}
private void ThicknessLevel8_Click(object sender, EventArgs e)
{
brushThickness = 8;
}
private void ThicknessLevel10_Click(object sender, EventArgs e)
{
brushThickness = 10;
}
private void ThicknessLevel12_Click(object sender, EventArgs e)
{
brushThickness = 12;
}
private void ThicknessLevel14_Click(object sender, EventArgs e)
{
brushThickness = 14;
}
private void FillColour_Click(object sender, EventArgs e)
{
ColorDialog fillColourDialog = new ColorDialog();
fillColourDialog.ShowDialog();
currentColor = fillColourDialog.Color;
panelArea.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
ColorDialog fillColourDialog = new ColorDialog();
fillColourDialog.ShowDialog();
currentBoarderColor = fillColourDialog.Color;
panelArea.Invalidate();
}
}
}
How do i stop the flickering?
*UPDATE:*This code actually works great when i'm drawing directly on the form. However, when i try to draw on the panel, flickering becomes an issue
Try to insert drawing logic in current form's
method. In this case you should use parameter e to get Graphics object. Use e.Graphics property. Then you should invoke Invalidate() method for this form whenever form must be redrawn. PS: DoubleBuffered must be set to true.
I have had the same problem. I was never able to 100% rid myself of the flicker (see point 2), but I used this
as well as
The main issue for flickering is making sure you
winforms invokes the
OnPaint
method each time the form needs to be redrawn. There are many ways it can be devalidated, including moving a mouse cursor over the form can sometimes invoke a redraw event.And important note about
OnPaint
, is you don't start from scratch each time, you instead start from where you were, if you flood fill the background color, you are likely going to get flickering.Finally your gfx object. Inside
OnPaint
you will need to recreate the graphics object, but ONLY if the screen size has changed. recreating the object is very expensive, and it needs to be disposed before it is recreated (garbage collection doesn't 100% handle it correctly or so says documentation). I created a class variableand then used it locally in
OnPaint
like so, but this was because I needed to use the gfx object in other locations in my class. Otherwise DO NOT DO THIS. If you are only painting in OnPaint, then please usee.Graphics
!!Hope this helps.
I'd advise overriding OnPaintBackground and handling the background erase yourself. If you know you are painting the whole control you can just do nothing in OnPaintBackground (don't call the base method) and it will prevent the background colour being painted first
If memory is tight (so you don't want the memory cost of double-buffering), one possible way to REDUCE, though not eliminate, flicker, is to set background color to the dominant color in your current scene.
Why this helps: flicker is a momentary flash of the background color, which the OS draws before drawing child controls or your custom drawing code. If that flash is a color that is closer to the final color to be displayed, it will be less noticeable.
If you are not sure what color to start with, start with 50% gray, because this is an average of black and white, so will be closer to most colors in your scene.
For a "cleaner solution" and in order to keep using the base Panel, you could simply use Reflection to implement the double buffering, by adding this code to the form that holds the panels in which you want to draw in
Where "DrawingPanel" is the name of the panel that you want to do the double buffering.
I know quite a lot of time has passed since the question was asked, but this might help somebody in the future.
here is the program of moving circle in .net, that doesn't flicker.