Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix panel flickering when redrawing?

Tags:

c#

panel

I have a panel that I've subclassed to and have set DoubleBuffered true, I constantly need to refresh the drawing but it flickers and have no idea why.

private delegate void MyDelegate();

public void heartBeat()
    {
        while (true)
        {
            if (map.processNubots(rules))
            {
                if (this.InvokeRequired)
                {
                    this.Invoke((MyDelegate)delegate
                    {
                        //drawPanel.SuspendLayout();
                        drawPanel.Refresh();
                        displayGrid();
                        //drawPanel.ResumeLayout();
                    });
                }
                Thread.Sleep(500);
            }
            else
            {
                break;
            }
        }
    }

    public void displayGrid()
    {
        int i = 0;
        foreach (DictionaryEntry pair in map)
        {
            Monomer current = (Monomer)pair.Value;
            drawMonomers(current.getLocation(), current.getState());
            i++;
        }
    }

    public void drawMonomers(Point location, string state)
    {
        ...

        SolidBrush sb = new SolidBrush(mycolor);
        SolidBrush sbt = new SolidBrush(Color.Black);
        Graphics g = drawPanel.CreateGraphics();
        Font text = new Font("Arial", scale / 2);
        Pen pen = new Pen(Color.Black, 1);
        pen.Alignment = PenAlignment.Inset;
        g.FillEllipse(sb, offSet + ((location.Y * scale) / 2) + (location.X * scale), offSet + (-location.Y * scale), scale, scale);
        g.DrawEllipse(pen, offSet + ((location.Y * scale) / 2) + (location.X * scale), offSet + (-location.Y * scale), scale, scale);
        g.DrawString(state, text, sbt, (offSet + ((location.Y * scale) / 2) + (location.X * scale)) + scale / 6, (offSet + (-location.Y * scale)) + scale / 6);

        sb.Dispose();
        sbt.Dispose();
        pen.Dispose();
    }

So after every "computation" and have added something to my imaginary grid, I need to update the panel to show this new item on my grid. I have tried invalidating the panel right before the displayGrid() function but it seems to cause even more flickering.

The heartbeat() function is currently being called on a separate thread.

Here is my new Panel class.

public class Display : Panel
{
    public Display()
    {
        this.DoubleBuffered = true;

    }
}
like image 784
David Chavez Avatar asked Jun 02 '13 12:06

David Chavez


2 Answers

    Graphics g = drawPanel.CreateGraphics();

Using CreateGraphics() and turning on double-buffering is the worst possible combination. CreateGraphics() gives you a Graphics object that draws directly to the screen. Double-buffering sets up a Graphics object that draws to a bitmap, the buffer used in double-buffering. Then renders the bitmap to the screen at the end of the paint cycle.

So what happens in your code is that you draw the screen directly, something you can barely see but visible if it is slow enough. Then right after that the buffer that you never draw into gets painted. Which wipes out what you drew before. The net effect is heavy flicker with your paint output visible for only a handful of milliseconds.

Using CreateGraphics() was the mistake. You always want to render through the e.Graphics object that you get from the Paint event so you'll render to the buffer. Pass that Graphics object to your drawMonomers() method. Thus:

public void drawMonomers(Graphics g, Point location, string state) {
   // Etc...
}

private void Display1_Paint(object sender, PaintEventArgs e) {
   //...
   drawMonomers(e.Graphics, loc, state);
}

In general, CreateGraphics() has very limited usefulness. You only ever use it when you want to draw to the screen directly and you can afford for whatever you draw to disappear. That is typically only useful in the kind of program that has a render loop that constantly runs, producing new output at a high rate like 20+ frames per second. Like a video game.

like image 169
Hans Passant Avatar answered Sep 23 '22 09:09

Hans Passant


Try replacing the Panel with a PictureBox. This worked for me.

like image 43
Mark Manyen Avatar answered Sep 22 '22 09:09

Mark Manyen