5

我正在尝试实现以下方法: void Ball::DrawOn(Gr​​aphics g);

该方法应绘制球的所有先前位置(存储在队列中),最后绘制当前位置。我不知道这是否重要,但我使用 g.DrawEllipse(...) 打印以前的位置,使用 g.FillEllipse(...) 打印当前位置。

问题是,正如您可以想象的那样,有很多绘图要做,因此显示屏开始闪烁很多。我一直在寻找一种双缓冲的方法,但我能找到的只有这两种方法:

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

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

在尝试使用第一个时,我收到一个错误,说明在此方法中,属性 DoubleBuffered 由于其保护级别而无法访问。虽然我不知道如何使用 SetStyle 方法。

当我拥有的所有访问权限都是对我在方法中作为输入获得的图形对象时,是否有可能加倍缓冲区?

提前致谢,

编辑:我创建了以下类

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));
        }
    }
}

}

球 drawOn 看起来像这样:

new BufferedBall(this).DrawOn(g);

这是你的意思吗?因为它还在闪烁?

4

5 回答 5

3

Form类具有公开为受保护的 DoubleBuffered 属性。http://msdn.microsoft.com/en-us/library/system.windows.forms.control.doublebuffered.aspx但由于您从中派生出您的表单,Form因此您可以使用它。

于 2010-06-18T14:06:30.823 回答
3
this.DoubleBuffered = true;

这很好,但您必须将此语句移至构造函数。双缓冲需要设置,Windows 窗体必须创建一个缓冲区,这必须在绘制事件运行之前完成。构造函数是理想的。

于 2010-06-18T15:12:35.863 回答
2

为 Control 派生类的双缓冲设置样式的更简单方法是使用反射。见这里:http ://www.csharp-examples.net/set-doublebuffered/

这将为您节省子类化控件以设置受保护属性的步骤。

于 2010-06-18T14:42:06.303 回答
1

您无需在每次重绘时都将 DoubleBuffered 设置为 true。绘图完成时不会禁用它。只需从 DrawOn 中删除该行并将其设置在构造函数或 Forms Designer 中并检查结果。将该值设置为 false 会产生明显的闪烁,而设置为 true 则不会。

我以计时器强制每毫秒重绘一次的形式尝试了您的代码,并注意到当 DoubleBuffered 为真时没有闪烁:

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);
}
于 2010-06-18T15:37:23.463 回答
0

另一个选项,我会为你抛出,是将你所有的绘图都绘制到一个位图上,然后在 OnPaint 方法中,你只需将该位图绘制到窗体上。

它的手册,但它可以让你完全控制。我已经在我的一些宠物项目上成功地使用了它。

您可能还想研究一下 XNA —— 这对您的项目来说可能有点矫枉过正,但您可以在 WinForms 中使用 XNA 作为渲染引擎。

于 2010-06-18T15:42:24.347 回答