双缓冲 C#
我正在尝试实现以下方法:void Ball::DrawOn(Graphics g);
该方法应该绘制球的所有先前位置(存储在队列中),最后绘制当前位置。我不知道这是否重要,但我使用 g.DrawEllipse(...)
打印以前的位置,并使用 g.FillEllipse(...)
打印当前位置>。
问题是,正如您可以想象的那样,需要完成大量绘图,因此显示屏开始频繁闪烁。我一直在寻找双缓冲的方法,但我能找到的只有这两种方法:
System.Windows.Forms.Control.DoubleBuffered = true;
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 (Point 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);
这是你的意思吗?因为它仍然闪烁?
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:
System.Windows.Forms.Control.DoubleBuffered = true;
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 (Point 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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
Form
类具有公开为受保护的 DoubleBuffered 属性。 http://msdn.microsoft.com/ en-us/library/system.windows.forms.control.doublebuffered.aspx 但由于您的表单是从Form
派生的,因此您可以使用它。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 fromForm
you can use it.这很好,但是您必须将此语句移至构造函数。双缓冲需要设置,Windows 窗体必须创建一个缓冲区,这必须在绘制事件运行之前完成。构造函数是理想的。
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.
为 Control 派生类的双缓冲设置样式的一种更简单的方法是使用反射。请参阅此处:http://www.csharp-examples.net/set-doublebuffered/
这将节省您仅仅为了设置受保护的属性而对控件进行子类化的步骤。
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.
您不需要每次重绘时都将 DoubleBuffered 设置为 true。绘制完成后,它不会被禁用。只需从 DrawOn 中删除该行并将其设置在构造函数或表单设计器中并检查结果即可。将值设置为 false 会产生明显的闪烁,而设置为 true 则不会。
我以计时器强制每毫秒重绘一次的形式尝试了您的代码,并注意到当 DoubleBuffered 为 true 时没有闪烁:
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:
我将为您提供的另一个选择是将所有绘图都绘制到位图上,然后在 OnPaint 方法中,您只需将该位图绘制到窗体上即可。
它是手动的,但它可以让您完全控制。我已经在我的一些喜欢的项目中使用它并取得了一些成功。
您可能还想研究一下 XNA——它对于您的项目来说可能有点大材小用,但您可以在 WinForms 中使用 XNA 作为渲染引擎。
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.