【问题标题】:Best practice for custom control painting in Winforms?Winforms中自定义控件绘制的最佳实践?
【发布时间】:2010-12-15 15:33:58
【问题描述】:

通常当我覆盖 OnPaint 方法时,我会在其中创建钢笔和画笔等,然后将它们处理掉。

我还在某处读到,不是重新创建这些钢笔和画笔等,而是将它们创建为静态成员一次,然后在表单关闭时处理它们一次,等等。

这是更好的做法吗?

有没有更好的方法?

我可以假设,由于 OnPaint 被调用了 1000 次(?)次,与只创建一次相比,这将为 GC 创建大量工作。

【问题讨论】:

    标签: c# .net winforms gdi+


    【解决方案1】:

    我上个月读到了interesting article,它建议将所有的绘画都放在一个单独的 BufferedGraphics 对象上,然后让 on_paint 方法从该对象直接复制到控件的图形对象。

    这样,on-paint 速度更快,并且您仅在发生重大变化(即线条移动或文本更改)时才更新 BufferedGraphics。

    【讨论】:

    • 嗯,不太好。它假设了一些不好的事情,比如绘制完整的控件而不是裁剪的矩形。大多数时候,这不会是最快的。此外,他正在手动创建双缓冲区,而不是使用系统提供的 ControlStyles.DoubleBuffer ,显然是没有原因的。将代码从您的 OnPaint 处理程序中取出可能会很好,但本文有更多不太好的“奖励”功能。
    【解决方案2】:

    我真的取决于你在画什么。如果您要绘制仅由于用户交互而重新绘制的东西,您可以不必担心性能问题并临时创建所有图形对象,即在需要时。

    确保您Dispose() 图形中所需的一切。钢笔、画笔、区域、字体。这些都是 GDI 对象,并通过 GDI 句柄绑定到系统中。

    如果您需要以某种方式进行动画处理或无需用户点击即可及时更改的图形,请在前面准备好所有图形对象并尽可能多地重复使用它们。可以在这里应用的规则是,在绘制动画的每一帧时最好浪费内存而不是毫秒。

    最后,至少在这篇文章中 - 不要忘记使用双缓冲,无论是在 .net 控制系统中自动还是回滚位图样式。

    玩得开心 GDI :)

    【讨论】:

      【解决方案3】:

      我要做的是将画笔和钢笔作为自定义控件的成员,然后在处理控件时处理它们。这样,您每次调用 OnPaint 时都可以重复使用相同的画笔/笔。

      我不会声明它们static,因为您无法知道何时可以处置您的物品。但正如 SLaks 所提到的,如果内存中同时有许多控件实例,最好将画笔和笔创建为静态的,这样在应用程序的生命周期内您只创建每个对象的一个​​实例.

      【讨论】:

      • 如果控件的实例很多,最好将它们设为静态。随身携带几把刷子并没有错。
      • 这很好,如果这个控件有很多实例,那么将画笔和钢笔设为静态会是个好主意。
      【解决方案4】:

      最佳做法是使用系统笔和画笔,因为它们已针对最少的资源消耗进行了优化。

      【讨论】:

      • 仅当他使用静态内置颜色时。
      【解决方案5】:

      如果画笔和钢笔不改变,那么创建一次并重复使用它们肯定会更好。但是请注意,如果您的控件可能在多个线程上使用(这不太可能),您应该将它们设为 ThreadStatic(并在每个线程首次使用时初始化)或将它们设为实例成员(并将它们放置在控件的Dispose 覆盖);否则,您将得到不可重现的 GDI+ 错误,因为 GDI+ 对象不能同时在多个线程上使用。图像也是如此。

      如果它们确实发生了变化(例如,如果您使用取决于控件大小的渐变画笔),您可能仍希望将它们存储在实例字段中,并在控件的大小(或其他)发生变化时重新创建它们。

      注意,顺便说一下,如果你使用普通颜色,你可以使用静态的BrushesPens 类,其中包含所有.Net 内置颜色的静态画笔和钢笔,以及SystemBrushesSystemPens 用于系统颜色。

      【讨论】:

      • 谢谢,还有一个问题:何时以及如何调用控件的 Dispose 方法?我以前从未在控件上调用过它。
      • 你不需要调用它; .Net 将在处置父控件时调用它(当您处置它所在的表单时)。如果它在您的主窗体上,它将在 Application.Run 调用结束时处理。
      • 谢谢斯拉克斯。还有一件事:)由于我通常只使用一个表单,如果我创建另一个不是主表单的表单,我必须在它关闭后处理它?
      • 表单只有在使用 .Show 或 Application.Run(或等效项)显示时才会在关闭时自动处理。使用 .ShowDialog 显示的表单在关闭时被释放。
      • @homeInAStar:是的,你是对的;我没有仔细阅读该方法。调用ShowDialog时,最好将代码包装在using语句中。
      猜你喜欢
      • 1970-01-01
      • 2015-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-16
      相关资源
      最近更新 更多