我需要创建多个Graphics对象吗?

3

我是一名老旧的Delphi程序员,习惯于创建对象并在整个过程中使用它们以实现高效的内存使用。但在C#中(也许是我看到的所有教程),每次都要用new创建东西(多亏了垃圾收集器!让我来编码)。

无论如何,我正在尝试创建一个设计软件,其中有很多绘图。我的问题是:我必须创建一个图形对象,还是在每次绘制事件中使用protected override void OnPaint(PaintEventArgs e) e.Graphics.. 因为当我创建一个图形对象然后调整我绘制的控件的大小时,我创建的图形对象会出现剪切问题,只会绘制旧的矩形区域。

谢谢


1
始终使用提供的“Paint”方法。使用CreateGraphics方法会导致灾难。 - DonBoitnott
1
好的,感谢您的快速回答。我一直在保留一个缓冲位图来进行绘制,如果发生像调整大小或绘制额外内容之类的事情;我会使用从它创建的图形对象重新绘制,然后使用graphics.DrawImage(bitmapB, 0, 0); 刷新到控件组件的表面上。 现在我将重新设计我的管道,并使用OnPaint事件的e.graphic对象进行刷新.. 再次感谢。您真的很有帮助。 - Ibrahim Ozdemir
4个回答

9
缓存对象在对象创建昂贵、存储廉价且相对简单易更新时是有意义的。然而,Graphics对象独特之处在于以下条件都不成立:
  • 创建它非常便宜,不到一微秒。
  • 存储它非常昂贵,底层设备上下文存储在会话的桌面堆中。可以存储的对象数量很少,不超过65535。在会话中运行的所有程序都共享该堆。
  • 保持更新非常困难,背后发生的事情会使设备上下文无效。例如,用户或您的程序更改窗口大小,从而使Graphics.ClipBounds属性无效。您正在浪费使用正确的Graphics对象的机会,即在Paint事件处理程序中传递给您的对象。特别是在使用双缓冲时,这会成为错误的源泉。
缓存Graphics对象是一个错误。

4

如果您想在表面上绘图,请始终使用来自Paint事件的Graphics对象!

如果您想在Bitmap中绘图,则创建一个Graphics对象并将其用于所需时间。

为了使Paint事件有效,您需要将所有绘图收集到List的图形操作中;因此,您需要创建一个不错的类来存储所需的所有参数。

在您的情况下,您可能需要考虑一种混合方法:旧的图形操作绘制到位图中,例如您控件的BackgroundImageImage

当前/正在进行的绘图是在表面上完成的。这相当于将位图用作缓存,因此您不必在每次微小更改时重新绘制大量操作等。

这与您的撤销/重做实现密切相关。您可以设置限制并在位图中绘制先前的动作以及在表面上绘制后面的动作。

附言:您还应重新考虑您的GC态度。它简单高效,并且是周围的福音。(是的,我也曾经做过我的TP&Delphi,当它们还是负担得起的时候..)——是的,我们进行编码,但GC不是关于编码而是有关清理的。最多有点无聊..(而且您始终可以进行设计以避免它,但不能在Windows系统中使用Graphics对象。)


2

每个实现IDisposable接口的类都应该尽快Dispose()它。确保您了解using(...){}语句。

在WinForms (GDI+) 中绘图的最佳实践确实是使用来自PaintEventArgs的Graphics对象。而且因为你没有创建它,所以不要Dispose()它。也不要将其存储。


-1

我必须完全不同意其他更有经验的成员们的说法,他们认为反复创建Graphics对象并没有什么大不了的,甚至更好。

HDC是指向HDC__结构体的指针,该结构体只有一个成员变量“int unused”。每次需要绘图时创建另一个实例/对象是绝对浪费和愚蠢的行为。HDC并不大,它只有4或8个字节,而它所指向的结构体在几乎所有情况下都是4个字节。此外,就某个人提出的观点而言,在WndProc()开头使用“static”关键字来创建Graphics对象并不能帮助你避免反复创建它,因为唯一给Graphics对象分配设备上下文或句柄以进行绘制的方法是调用它的构造函数,所以“static”对于你避免反复创建它毫无作用。

此外,微软建议您创建一个HDC指针,并将其分配给每个单独的WM_PAINT消息已经具有的PAINTSTRUCT相同的值。

很抱歉,但在我看来,WinAPI非常糟糕。举个例子,我花了一整天的时间研究如何创建一个子WS_EX_LAYERED窗口,结果发现为了启用Win 8功能,必须在XML中添加带有操作系统ID号的代码到清单中。这太荒谬了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接