How to fix panel flickering when redrawing?

14,046

Solution 1

    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.

Solution 2

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

Share:
14,046

Related videos on Youtube

David Chavez
Author by

David Chavez

Updated on September 15, 2022

Comments

  • David Chavez
    David Chavez over 1 year

    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;
    
        }
    }
    
  • David Chavez
    David Chavez almost 11 years
    In my program I must compute some interactions before I drawMonomers, how would I be able to keep processing, then redrawing using this method?
  • user1703401
    user1703401 almost 11 years
    Store whatever you compute in fields of your class so you'll have them readily available when it is time to paint. If that computation hasn't completed yet then don't call drawMonomers() yet. Call the panel's Invalidate() method as soon as that's done.
  • David Chavez
    David Chavez almost 11 years
    this computation is looping. compute objects in grid -> paint, compute objects in grid -> paint. As I understand the Paint is only done when it first loads the panel.
  • user1703401
    user1703401 almost 11 years
    Painting is done when required. Or when you force it, like you did in your code snippet by calling Refresh().