Adraria
Adraria

Reputation: 93

Winforms Graphics flickering. (Double buffering doesn't help!)

I'm trying to create a simple Windows Forms graphics app that basically will draw a circle every time the user clicks and it expands, while slowly fading away.

When I tried to use the Paint() Event for my graphics functionality, nothing happened, so I created a separate function called "Render" that is called in my main update Timer.

The app worked but the graphics flickered. After some researched I realized that I had to enable Double Buffering so that it would render to a buffer and then the buffer would be rendered to the screen.

The flickering still didn't stop!
Is this because double buffering only works for Paint() events and if so how do I get the Paint() event to work or am I not enabling Double Buffering right?

Here's my code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Widget
{

public class Circle
{
    public float X;
    public float Y;
    public float Radius;
    public int Alpha;

    public Circle(float X, float Y, float Radius, int Alpha)
    {
        this.X = X;
        this.Y = Y;
        this.Radius = Radius;
        this.Alpha = Alpha;
    }
}

public partial class Form1 : Form
{
    public static readonly int ScreenX = Screen.PrimaryScreen.Bounds.Width;
    public static readonly int ScreenY = Screen.PrimaryScreen.Bounds.Height;

    public int WindowWidth = 500, WindowHeight = 500;
    public Graphics G;
    private Pen Pen;

    private Timer timer = new Timer();
    private List<Circle> Circles;

    public Form1()
    {
        this.Text = "Widget - Sam Brandt";
        this.Size = new Size(WindowWidth, WindowHeight);
        this.StartPosition = FormStartPosition.Manual;
        this.Location = new Point(ScreenX - WindowWidth - 100, 0);
        this.FormBorderStyle = FormBorderStyle.FixedSingle;
        this.MaximizeBox = false;
        this.Icon = new Icon("C:\\Users\\Admin\\Desktop\\Code Repositories\\Visual Studios\\Widget\\Widget\\Properties\\WidgetIcon.ico");
        Pen = new Pen(Color.Black, 1);
        G = CreateGraphics();
        //this.Paint += new PaintEventHandler(OnPaint);
        ConstructMouse();
        FormWithTimer();
        DoubleBuffered = true;

        Circles = new List<Circle>();
    }

    public void ConstructMouse()
    {
        this.MouseUp += new MouseEventHandler(OnMouseUp);
        this.MouseMove += new MouseEventHandler(OnMouseMove);
        this.MouseDown += new MouseEventHandler(OnMouseDown);
    }

    public void FormWithTimer()
    {
        timer.Tick += new EventHandler(timer_Tick);
        timer.Interval = (10);
        timer.Enabled = true;
        timer.Start();
    }

    protected void OnMouseUp(object sender, MouseEventArgs e)
    {
    }

    protected void OnMouseMove(object sender, MouseEventArgs e)
    {
    }

    public void OnMouseDown(object sender, MouseEventArgs e)
    {
        Circles.Add(new Circle(e.Location.X, e.Location.Y, 0, 255));
    }

   /*public void OnPaint(object sender, PaintEventArgs e)
    {
        e.Graphics.Clear(Color.White);
        for (int i = 0; i < Circles.Count; i++)
        {
            Circle C = Circles[i];
            e.Graphics.DrawEllipse(new Pen(Color.FromArgb(C.Alpha, 0, 0, 0), 1), C.X - C.Radius, C.Y - C.Radius, 2 * C.Radius, 2 * C.Radius);
        }
    }*/

    private void Tick()
    {
        for (int i = 0; i < Circles.Count; i++)
        {
            Circle C = Circles[i];
            C.Radius++;
            C.Alpha -= 3;
            if (C.Alpha == 0)
            {
                Circles.RemoveAt(i);
            }
        }
    }

    public void Render()
    {
        G.Clear(Color.White);
        for (int i = 0; i < Circles.Count; i++)
        {
            Circle C = Circles[i];
            G.DrawEllipse(new Pen(Color.FromArgb(C.Alpha, 0, 0, 0), 1), C.X - C.Radius, C.Y - C.Radius, 2 * C.Radius, 2 * C.Radius);
        }
    }

    public void timer_Tick(object sender, EventArgs e)
    {
        Render();
        Tick();
    }
}
}

Upvotes: 1

Views: 3161

Answers (4)

codefox
codefox

Reputation: 120

Try this (in VB):

Dim aProp = GetType(Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance)
aProp.SetValue(Me, True, Nothing)

Also had problems with double buffering, and usual setting property DoubleBuffered to true do not work. I have found this solution somewhere on web

Upvotes: 0

Tim
Tim

Reputation: 887

I'd do all your drawing to a separate Graphics object, and then copy that to your main Graphics object on the timer tick event only if there's been a change. You may need to keep track of that with a boolean member. This means your background drawing will have to be triggered by some other event.

If your picture is actually changing every 10 milliseconds, I'd slow down the timer a bit and set it to 50 milliseconds.

Upvotes: 0

Ivan Stoev
Ivan Stoev

Reputation: 205849

Short answer - keep DoubleBuffered = true and use Paint event.

When I tried to use a PaintEvent for my graphics functionality, nothing happened

When you do some modifications and want to reflect them, use Control.Invalidate method, which according to the documentation

Invalidates the entire surface of the control and causes the control to be redrawn.

In your case, something like this

void timer_Tick(object sender, EventArgs e)
{
    Tick();
    Invalidate();
}

Upvotes: 4

T McKeown
T McKeown

Reputation: 12857

More observations here but probably will be the answer.

  • Why Timer?
  • Use the Paint event, it is called when the GDI+ determines it needs to, you are constantly painting with your code as-is.
  • Your code makes it look like you are not using double buffering.

Upvotes: 0

Related Questions