2012-01-25 61 views
2

我想让自己了解一些WinForm图形知识,所以我将我的小2D编辑器从XNA改写为WinForm Graphics-only。使用Winforms进行绘制

现在,我为自己制作了一个用于tileset的新用户控件,但正如我所见,Paint方法仅在控件初始化时调用。由于我想永久重画我的控件(或至少通过MouseOver事件来节省一点性能),所以我听说Invalidate()方法让控件重绘本身,但这是方式太不合格。

有什么办法让我的UserControl通过代码绘制自己,而没有这些性能问题?

回答

3

Paint方法是而不是只在控件初始化时调用。每次控件需要重新绘制时都会调用它。这当然是在控件第一次创建时发生的。当你的应用程序被最小化,然后被恢复时,当另一个窗口移过你的应用程序,遮住它的内容然后被移除时,也会发生这种情况,等等。当您使用Invalidate方法或同等方法使控件的客户区失效时也会发生这种情况。这是在Windows开发早期作为性能优化完成的 - 不需要重新绘制没有改变的东西!

如果要强制重新绘制控件,应调用Invalidate method并指定要重新绘制的客户区的特定区域。

我不知道你的意思是“那是方式太不合格了”。对于Invalidate方法来说,这是不可能的。它所做的只是设置一个标志,告诉Windows你的控制需要在空闲时重新绘制(不处理任何其他消息)。

如果要强制Windows重新绘制控件立即(无需等待它是空闲的;内置于Windows从成立之初的另一个性能优化),调用Update method,这迫使所有无效立即重绘区域。

如果您的代码在Paint事件处理程序方法内缓慢,唯一的方法可能会很慢。显然,我不能告诉你如何优化代码而不先看。


反正是有让我的用户通过代码油漆本身,而无需这些性能问题?

Paint事件恰好是控件应该如何以及在哪里绘制自己。这就是为什么它在那里。

如果做的Paint事件不油漆,任何你画该控件重绘(正如前面提到的,可以应对任何数量的发生的预期和非预期接下来的时间将被清除事件)。

但是,有时将临时对象绘制到控件的客户区域(例如响应MouseDown事件显示拖动矩形)是有意义的。在这种情况下,您可以随时获得Graphics类的实例(它通常作为参数传递给您的Paint事件处理程序方法,并在其上调用方法来执行绘图)。您可以通过调用控件上的CreateGraphics method来完成此操作,该控件返回Graphics对象。然后,您可以绘制到Graphics对象上,就像在Paint事件处理程序方法中一样。

显然,这不能/不会出现任何超过Paint事件处理方法里面的绘制代码更快(如果这是事实上的罪魁祸首),但它会引起画面立即更新而比每当控制空闲并且不处理任何其他消息。

我会再次重申,这种做法应该被使用立即提供和临时反馈,因为一切你画在下一次重绘控件将被删除。当发生这种情况时,会引发Paint事件,并且您的代码在该方法处理程序中运行,它不知道您在其他一次性事件上绘制的内容。这就是为什么一切都应该发生在Paint事件处理程序方法内部,并且当某个其他事件需要重新绘制时,应该调用Invalidate(也可能通常不是,但通常不是Update)。

+0

首先 - 感谢您的回答。那么,也许这不是一个性能问题,但我的MouseMove方法等待控件重新绘制的时间?我的控件中有一个ScrollY变量,它应该调整图形的位置,并由其侧面的vScrollbar进行调整。所以我有一个vScrollbar_Scroll-Event,它更新了ScrollY值并调用了滚动条的Invalidate-Method。但它只是非常缓慢。我究竟做错了什么? –

+0

好的,感谢您的建议,我进一步研究并解决了这个问题。我将ControlStyles.AllPaintingInWmPaint和ControlStyles.OptimizedDoubleBuffer标志设置为true,并减少了我的绘画区域和一切工作非常流畅。谢谢! –

+1

@哈拉尔德:嗯,是的。双缓冲是平滑UI更新的标准方法。通常它是针对闪烁指示的,而不是你所描述的“慢”。 “AllPaintingInWmPaint”意味着控件不会首先通过处理WM_ERASEBKGND消息并绘制默认颜色来消除自己。双缓冲由WinForms提供。它首先将所有内容绘制到屏幕外的缓冲区中,然后将屏幕外缓冲区直接屏蔽到屏幕上。这很有帮助,因为您看不到所有的中间绘画步骤。 –