C#中Graphics的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#中Graphics的问题相关的知识,希望对你有一定的参考价值。

在C#的Winform应用程序中,为什么不能在构造函数和Form_load中用Graphics绘图呢?而只能卸载Paint事件或者控件的点击事件中。
我觉得原因是因为每次窗体加载完显示的时候会刷新,这样绘制的东西就没有了,那为什么构造函数的InitializeComponent( )中加载的控件能够显示,这些控件的底层还不是用GDI+绘制的,我想原因应该是这些控件的界面绘制部分的代码也在它们各自的Paint事件中。
请高手点评我的想法是否正确,谢谢!
还有,为什么我在窗体的构造函数或者Form_Load时间使用this.hide(),窗体
还是显示出来,而在Paint或者控件的点击事件中使用this.hide,窗体就不见了,而且再也不出现了,我想和上面的关于Graphics绘制部分的原理可能有点关系,但是我也绕不清楚,请高手指教!

第一个说对了.因为Load发生的时候窗体并未显示,等到窗体刷新时就没了.
加载的控件能显示,是因为控件不是图像,它们会在各自的Paint过程中绘制.(具有系统外观的控件是由系统负责绘制的)
在构造函数或Form_Load过程中this.hide会无效,是因为此时窗体并未显示出来,在这之后窗口随时会被重新激活(这中间逻辑蛮复杂的,我也没有研究太懂,主要涉及到SetVisibleCore,啥时候有空了仔细研究下).关于这个,可以考虑采用其它办法.用Google搜索一下"SetVisibleCore 启动时不显示"能找到些资料.但是这个和Graphics绘制没有关系.
参考技术A 这个吧。我认为是Form_Load事件的触发时间的问题吧,应该是Form_Load事件先于其他事件触发,所以你在Form_Load中Hide()时候,窗体本就没显示出来,你在Form_Load中画图的时候,它确实画了。然后Paint的时候可能是覆盖了吧。

个人猜想,没环境,所以没验证过。楼主找资料查下告诉我。。
你可以看看Form的设计文件,那个Form1.Design.cs或者.vb什么的。找下看看。
唉。学艺不精,还边学边往,以前看过一个说明窗体中各个事件调用顺序的文章,看完就忘了。。这毛病啥时候能该。。。
参考技术B 你试试CreateGraphics方法。。。

Button1.CreateGraphics();
参考技术C 好像是被窗口上的控件盖住了,你打空间背景设透明看看还有没有线,我以前做过一个作业就是在Form_load上画了坐标,picturebox设透明就能看到窗体上画的坐标了

我是不是必须继续创建 Graphics 对象

【中文标题】我是不是必须继续创建 Graphics 对象【英文标题】:Do I have to keep creating a Graphics object我是否必须继续创建 Graphics 对象 【发布时间】:2015-09-14 22:30:13 【问题描述】:

我是一个老的 delphi 程序员,我习惯于创建对象并一直使用它们来有效地使用内存。但是在 c# 中(可能是我见过的所有教程),你每次都在用new 创建东西(感谢垃圾收集器!!,让我来编码)..

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

谢谢

【问题讨论】:

始终使用提供的Paint 方法。 CreateGraphics 是通往灾难的必经之路。 好的,感谢您的快速回答。我保留了一个缓冲区位图来绘制它,如果发生调整大小或绘制额外的东西等事情;我正在使用从中创建的图形对象进行重绘,然后使用 graphics.DrawImage(bitmapB, 0, 0); 将其刷新到控制组件的表面。现在我将重新设计我的管道并使用 OnPaint 事件的 e.graphic 对象来刷新......再次感谢。你真的很有帮助 【参考方案1】:

当对象的创建成本高、存储成本低且保持更新相对简单时,缓存对象才有意义。 Graphics 对象的独特之处在于,没有这些条件为真:

创建起来非常便宜,不到一微秒。 存储非常昂贵,底层设备上下文存储在会话的桌面堆中。可以存储的对象数量很少,不超过 65535。在会话中运行的所有程序共享该堆。 很难保持更新,背后发生的事情会使设备上下文无效。就像用户或您的程序更改窗口大小一样,会使 Graphics.ClipBounds 属性无效。您正在浪费使用正确 Graphics 对象的机会,该对象是在 Paint 事件处理程序中传递给您的。尤其是使用双缓冲时的错误工厂。

缓存 Graphics 对象是一个错误。

【讨论】:

【参考方案2】:

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

如果您想绘制到 Bitmap 中,您可以创建一个 Graphics 对象并根据需要使用它。

要使Paint 事件起作用,您需要将所有绘图收集到List 的图形操作中;所以你会想要创建一个很好的类来存储所有需要的参数。

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

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

这与您的undo/redo 实现密切相关。您可以设置一个限制并将之前的内容绘制到 Btimap 中,将之后的内容绘制到表面上..

PS:您还应该重新考虑您的GC 态度。拥有它简单、高效,是一种幸福。 (而且,是的,我已经完成了 TP&Delphi 的份额,早在它们负担得起的时候......) - 是的,我们进行编码,但 GC 不是关于编码,而是关于家务。无聊至多..(你总是可以设计来避免它,但不能在 Windows 系统中使用 Graphics 对象。)

【讨论】:

【参考方案3】:

每个实现 IDisposable 的类的一般规则是尽快 Dispose() 它。确保您了解using(...) 声明。

对于在 WinForms (GDI+) 中绘图,最佳实践确实是使用 PaintEventArgs 中的 Graphics 对象。因为你没有创建那个,所以 not Dispose() 它。也不要把它藏起来。

【讨论】:

【参考方案4】:

我必须完全不同意这里其他更有经验的成员,他们说一遍又一遍地重新创建 Graphics 对象没什么大不了的,或者实际上更好。

HDC 是一个指向 HDC__ 结构的指针,它是一个具有一个成员“int未使用”的结构。每次需要绘制时创建另一个实例/对象是绝对的浪费和愚蠢。 HDC 并不大,它是 4 或 8 字节,它指向的结构几乎在所有情况下都是 4 字节。此外,就一个人所做的而言,在切换前的 WndProc() 开头使用“静态”关键字制作图形对象也无济于事,因为为图形对象提供设备的唯一方法要绘制的上下文或句柄是通过调用其构造函数来实现的,因此“静态”不会让您避免一遍又一遍地创建它。

除此之外,Microsoft 建议您创建一个 HDC 指针并将其分配给 PAINTSTRUCT 已经具有的相同值,它发送的每条 WM_PAINT 消息。

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

【讨论】:

以上是关于C#中Graphics的问题的主要内容,如果未能解决你的问题,请参考以下文章

C#中Graphics的问题

C#画图——Graphics

C# Graphics的用法?

C# 一个关于Graphics类的问题

c#中e.Graphics.DrawString

C#中基于GDI+(Graphics)图像处理系列之前言