views:

142

answers:

5

Hey,

I'm trying to implement the following method: void Ball::DrawOn(Graphics g);

The method should draw all previous locations(stored in a queue) of the ball and finally the current location. I don't know if that matters, but I print the previous locations using g.DrawEllipse(...) and the current location using g.FillEllipse(...).

The question is, that as you could imagine there is a lot of drawing to be done and thus the display starts to flicker much. I had searched for a way to double buffer, but all I could find is these 2 ways:

1) System.Windows.Forms.Control.DoubleBuffered = true;

2) SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);

while trying to use the first, I get the an error explaining that from in this method the Property DoubleBuffered is inaccessible due to its protection level. While I can't figure how to use the SetStyle method.

Is it possible at all to double buffer while all the access I have is to the Graphics Object I get as input in the method?

Thanks in Advance,

Edit: I had created the following class

namespace doubleBuffer
{
    class BufferedBall : System.Windows.Forms.Form{
        private Ball ball;
        public BufferedBall(Ball ball)
        {
            this.ball = ball;
        }

    public void DrawOn(Graphics g){
        this.DoubleBuffered = true;
        int num = 0;
        Rectangle drawArea1 = new Rectangle(5, 35, 30, 100);
        LinearGradientBrush linearBrush1 =
        new LinearGradientBrush(drawArea1, Color.Green, Color.Orange, LinearGradientMode.Horizontal);
        Rectangle drawArea2 = new Rectangle(5, 35, 30, 100);
        LinearGradientBrush linearBrush2 =
           new LinearGradientBrush(drawArea2, Color.Black, Color.Red, LinearGradientMode.Vertical);
        foreach (PointD point in ball.previousLocations)
        {
            Pen myPen1;
            if (num % 3 == 0)
                myPen1 = new Pen(Color.Yellow, 1F);
            else if (num % 3 == 1)
                myPen1 = new Pen(Color.Green, 2F);
            else
                myPen1 = new Pen(Color.Red, 3F);
            num++;
            myPen1.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
            myPen1.StartCap = System.Drawing.Drawing2D.LineCap.RoundAnchor;
            myPen1.EndCap = System.Drawing.Drawing2D.LineCap.AnchorMask;
            g.DrawEllipse(myPen1, (float)(point.X - ball.radius), (float)(point.Y + ball.radius), (float)(2 * ball.radius), (float)(2 * ball.radius));
        }
        if ((ball.Host.ElapsedTime * ball.Host.FPS * 10) % 2 == 0){
            g.FillEllipse(linearBrush1, (float)(ball.Location.X - ball.radius), (float)(ball.Location.Y + ball.radius), (float)(2 * ball.radius), (float)(2 * ball.radius));
        }else{
            g.FillEllipse(linearBrush2, (float)(ball.Location.X - ball.radius), (float)(ball.Location.Y + ball.radius), (float)(2 * ball.radius), (float)(2 * ball.radius));
        }
    }
}

}

and the ball drawOn looks like this:

new BufferedBall(this).DrawOn(g);

Is that what you meant? because it is still flickering?

+2  A: 

Form class has DoubleBuffered property exposed as protected. http://msdn.microsoft.com/en-us/library/system.windows.forms.control.doublebuffered.aspx but since you derive your form from Form you can use it.

Andrey
No, it's protected.
LmSNe
yes, but you are deriving from Form. Like when you create basic winforms app it creates Form1 that comes from Form. You can access it from there. If not - you can always create wrapper that exposes this property.
Andrey
A: 

A simpler way to set style for double buffering of Control-derived classes is to use reflection. See here: http://www.csharp-examples.net/set-doublebuffered/

That would save you the step of subclassing a control just to set a protected property.

Paul Sasik
But that would need access to the from object I'm drawing on, right?All the access I have is to the Graphics object that is supplied as a parameter to the function. Is reflection possible in such a situation?
LmSNe
Yes. It can be done via reflection with just a few lines of code: http://stackoverflow.com/questions/76993/how-to-double-buffer-net-controls-on-a-form
Paul Sasik
+1  A: 
this.DoubleBuffered = true;

That's fine but you have to move this statement to the constructor. Double-buffering requires setup, Windows Forms has to create a buffer, that must be done before the paint event runs. The constructor is ideal.

Hans Passant
A: 

You don't need to set DoubleBuffered to true each time you redraw. It is not disabled when drawing finished. Just remove the line from DrawOn and set it in the constructor or Forms Designer and check the results. Setting the value to false produces significant flickering while setting to true doesn't.

I tried your code in a form where a timer forces a redraw every millisecond and noticed no flickering when DoubleBuffered is true:

private int yDir = 1,xDir=1;
int step = 1;

private void timer1_Tick(object sender, EventArgs e)
{
    if (ball.Location.Y >= this.Height-50)
        yDir =   -1 ;
    if (ball.Location.X >= this.Width-50) 
        xDir= -1 ;

    ball.MoveBy(xDir*step,yDir*step);
    ball.Host.ElapsedTime++;
    this.Invalidate();
 }

private void DoubleBufferedBall_Paint(object sender, PaintEventArgs e)
{                      
        DrawOn(e.Graphics);
}
Panagiotis Kanavos
A: 

Another option, that I'll toss out for you, is to do all your drawing to a Bitmap, and then in the OnPaint method, you simply draw that Bitmap to the form.

Its manual, but it gives you full control. I've used it with some success on some pet projects of mine.

You also might want to look into XNA -- it might be overkill for your project here, but you can use XNA in WinForms as a rendering engine.

Nate Bross
Is that what you meant?public void DrawOn(Graphics g){ Bitmap bitmap = new Bitmap(800, 600, g); Graphics g1 = Graphics.FromImage(bitmap); // drawing to g1 ... g.DrawImage(bitmap, new Point(0, 0));}Because this still flickers.
LmSNe
Yes, it is, but I'm not sure why it still flickers for you.
Nate Bross