locked
Using DoubleBuffered with Panel RRS feed

  • Question

  • Hi,

    I need to draw some action on Panel and also avoid flickering. I have found some information about double buffering and drawing to BufferedGraphics object firstly and then render the content of BufferedGraphics object into associated Graphics object. I have created simple Windows Forms application to make me more familiar to this technic. But I can't understand some things on the beginning.

    I will past some parts of my code and then try to explain my questions:
    class DoubleBufferedPanel : System.Windows.Forms.Panel
    {
        private Graphics g;
        public Graphics getGraphics()
        {
            return this.g;
        }
    
        public DoubleBufferedPanel()
        {
            this.g = this.CreateGraphics();
            this.DoubleBuffered = true;
        }
    }
    The class above is my own Panel that inherits from System.Windows.Forms.Panel and is created to make posibility to set DoubleBuffered property which is protected in System.Windows.Forms. Control class.

    private DoubleBufferedPanel dbPanel;
    
    private void InitializeComponent2()
    {
        this.dbPanel = new DoubleBufferedPanel();
    
        this.SuspendLayout();
    
        //
        // dbPanel
        //
        this.dbPanel.Name = "dbPanel";
        this.dbPanel.Location = new System.Drawing.Point(3, 3);
        this.dbPanel.Size = new System.Drawing.Size(270, 270);
        this.dbPanel.BackColor = System.Drawing.Color.LightBlue;
        this.dbPanel.Paint += new System.Windows.Forms.PaintEventHandler(dbPanel_Paint);
    
        //
        // Form1
        //
        this.Controls.Add(this.dbPanel);
    
        this.ResumeLayout(false);
    }
    
    The code above is part of Form class. I have written InitializeComponent2() to not to make changes in InitializeComponent() method generated by Visual Studio. So the contructor of Form class looks like this:
    public Form1()
    {
        InitializeComponent();
        InitializeComponent2();
    }
    For now I just have the Form and the Panel control on this form.

    Then I tried to draw something ona my panel. This is content of Paint event:
    BufferedGraphics bg;
    BufferedGraphicsContext bgcontext;
    private void dbPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { this.bgcontext = new BufferedGraphicsContext(); this.bg = bgcontext.Allocate(this.dbPanel.getGraphics(), this.dbPanel.DisplayRectangle); this.bg.Graphics.DrawEllipse(new Pen(new SolidBrush(Color.Yellow)), this.dbPanel.DisplayRectangle); this.bg.Render(this.dbPanel.getGraphics()); this.bg.Dispose(); this.bgcontext.Dispose(); }

    The result of this code is the Form with lightblue Panel on it. But there is no ellipse on Panel.
    When I disable (set to false) DoubleBuffered property in my DoubleBufferedPanel class the result is the Form with Panel with black background and yellow ellipse.

    These are my questions:
    # if DoubleBuffered is set to true:
    - Why there is no result of drawing (no ellipse)? There is just panel with its background color (lightblue).

    # if DoubleBuffered is set to false:
    - Why the background color (lightblue) of my panel disappear? There is yellow ellipse on the black background.

    Finally I need to draw the crossroad and moving cars (rectangles) on the panel.
    My concept is:
    - draw crossroad (streets, crosswalks, etc.) to one BufferedGraphics object
    - draw cars to another BufferedGraphics object
    - render content of buffers to Graphics object associated with my panel

    I will need to refresh the panel very often to show the moving of cars, that is why I am trying to use drawing with buffer.
    But firstly I need to understand what happens in the apllication I have mantioned in this topic.

    Thank You for Your replies.
    Tuesday, June 9, 2009 4:22 PM

Answers

  • Yes you do the drawing when double buffered.

    //this.DoubleBuffered = true;

    bg.Graphics.Clear(Color.LightBlue);
    bg.Graphics.DrawEllipse(new Pen(new SolidBrush(Color.Yellow)), this.dbPanel.DisplayRectangle);

    David
    • Marked as answer by grafi Wednesday, June 10, 2009 10:51 AM
    Tuesday, June 9, 2009 5:46 PM
  • Not sure if it is clear from the previous posts, but you are helping too much.  A Control will automatically use the BufferedGraphicsXxxx classes.  All you need is this:

    using System;
    using System.Windows.Forms;

    public class BufferedPanel : Panel {
        public BufferedPanel() {
            this.DoubleBuffered = true;
        }
    }
    Hans Passant.
    • Marked as answer by grafi Wednesday, June 10, 2009 10:51 AM
    Tuesday, June 9, 2009 6:31 PM
  • Yes, that cannot work.  You have to use e.Graphics.
    Hans Passant.
    • Marked as answer by grafi Wednesday, June 10, 2009 10:51 AM
    Wednesday, June 10, 2009 10:39 AM

All replies

  • I suspect it's because you're not using BufferedGraphicsContext.Current.

    If DoubleBuffered is true, WM_ERASEBKGND (PaintBackground event) is not sent to the window, so no background is drawn. When you turn on the double buffer, all painting is assumed to happen in the paint handler. Using the DoubleBuffered property is redundant (and causes an extra full-window blit). Use either DoubleBuffered, or a BufferedGraphicsContext (and set the AllPaintingInPaint style).

    Read this:
    http://msdn.microsoft.com/en-us/library/b367a457.aspx

    One nitpicky note: It's considered (by many) to be "bad form" for an object to subscribe to its own events. Use the OnXXX overrides instead (OnPaint in this case). I know.. when you double-click on a form in the designer, the designer injects code to subscribe to the form's own Load event. Someone needs to spank the Form Designer guy (I think he's a VB'er). Why is it "bad form"? It's just a sensless waste of resources (a few cycles) to invoke the event mechanism for something you can just do directly in code. Also since calling the base.OnXXX method performs the event invocation (if there are any subscribers), you can control if you want to perform the action before or after outsiders have been notified.

    Tuesday, June 9, 2009 5:21 PM
  • Yes you do the drawing when double buffered.

    //this.DoubleBuffered = true;

    bg.Graphics.Clear(Color.LightBlue);
    bg.Graphics.DrawEllipse(new Pen(new SolidBrush(Color.Yellow)), this.dbPanel.DisplayRectangle);

    David
    • Marked as answer by grafi Wednesday, June 10, 2009 10:51 AM
    Tuesday, June 9, 2009 5:46 PM
  • Not sure if it is clear from the previous posts, but you are helping too much.  A Control will automatically use the BufferedGraphicsXxxx classes.  All you need is this:

    using System;
    using System.Windows.Forms;

    public class BufferedPanel : Panel {
        public BufferedPanel() {
            this.DoubleBuffered = true;
        }
    }
    Hans Passant.
    • Marked as answer by grafi Wednesday, June 10, 2009 10:51 AM
    Tuesday, June 9, 2009 6:31 PM
  • I have one more question about DoubleBuffered=true technic:

    If I set DoubleBuffered property to true where I should draw shapes.
    When I try to draw it in Paint event, there is no result on the panel. (I am not using BufferedGrpahics objects now).

    private void dbPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    {            
        this.dbPanel.getGraphics().DrawEllipse(new Pen(Color.Yellow), this.dbPanel.DisplayRectangle);
    }

    getGraphics() is method that returns Graphics object associated with dbPanel .

    There is no ellipse on the panel. Panel is clear.


    grafi
    • Edited by grafi Wednesday, June 10, 2009 10:34 AM
    Wednesday, June 10, 2009 10:32 AM
  • Yes, that cannot work.  You have to use e.Graphics.
    Hans Passant.
    • Marked as answer by grafi Wednesday, June 10, 2009 10:51 AM
    Wednesday, June 10, 2009 10:39 AM