【问题标题】:Why does PaintEventArgs.Graphics behave differently from Control.CreateGraphics?为什么 PaintEventArgs.Graphics 的行为与 Control.CreateGraphics 不同?
【发布时间】:2011-11-26 09:24:40
【问题描述】:

我编写了一个事件处理程序方法并将其附加到FormPaint 事件(只是主窗口)。此事件发送一个PaintEventArgs,其中包含一个名为Graphics 的属性,它是System.Drawing.Graphics 的一个实例。

这是我正在使用的代码:

private void Form1_Paint(object sender, PaintEventArgs e) {

    Bitmap bm = new Bitmap("fruit-dealer-full.jpg");

    Graphics g1 = this.CreateGraphics();
    Graphics g2 = e.Graphics;

    // g1.DrawImage(bm, 0, 0, this.Width, this.Height);
    // g1.DrawRectangle(
    //       Pens.White, 10.0f, 10.0f, this.Width - 200, this.Height - 200);

    g2.DrawImage(bm, 0, 0, this.Width, this.Height);
    g2.DrawRectangle(
           Pens.White, 10.0f, 10.0f, this.Width - 200, this.Height - 200);

}

最终我只是想更好地了解这里发生的事情,但具体来说,我有以下三个问题:

  1. 为什么g1在整个窗口中重绘图像,而g2只绘制新的部分,即使我在绘制之前调用g2.Clear()
  2. 为什么对于Graphics 对象,图像仅在窗口增大时才重绘,而不是在窗口变小时时重绘?
  3. 如果PaintEventArgs.Graphics 可以(或不应该)用于绘图,它的用途是什么?我想如果不需要重新绘制表单,它只会阻止您创建一个新的 Graphics 实例;我还缺少什么吗?

【问题讨论】:

  • 刚注意到ResizeRedraw控件样式标志,大概会用在这种情况下,不过还是想明白上面的意思!

标签: c# .net winforms


【解决方案1】:

.NET WinForms 孩子真的应该学习 Win32 API。已经拿起了 Petzold 的副本!

为什么g1在整个窗口中重新绘制图像,而g2只绘制新的部分,即使我在绘制之前调用g2.Clear()?

大概g2 是从BeginPaint 接收的设备上下文的包装器。我想 WinForms 会为您包装 PAINTSTRUCT::rcPaint - 这个变量描述了要绘制的区域。这是预期的行为 - 而不是每次另一个窗口与它重叠一个像素时都重新绘制整个窗口,而不是消耗 CPU 周期,您可以重绘......只是一个像素!

g2.Clear 大概受rcPaint 的限制。

g1 可能是窗口的GetDC - 它为您提供了整个表面以供绘制。

为什么对于任何一个 Graphics 对象,图像仅在窗口增大时才重绘,而不是在窗口变小时时重绘?

底层窗口类可能没有CS_HREDRAWCS_VREDRAW 窗口样式。没有这些,默认行为就没有理由要求您在窗口变小时重新绘制窗口:Windows 知道整个窗口的样子,它可以剪掉不需要的部分。这与窗口变大时不同,它不知道在新区域中绘制什么。

如果 PaintEventArgs.Graphics 可以(或不应该)用于绘图,它的用途是什么?我想如果不需要重绘表单,它只会阻止您创建新的 Graphics 实例;还有更多我想念的吗?

它用于绘图。除非您有一些复杂的绘图要求,否则您可以使用PaintEventArgs.Graphics 最大限度地减少您窗口上的绘画量。 (如上 - 这是一个巨大的 CPU 周期节省,它可能是一个简单的包装 BeginPaintEndPaint - 这是how drawing to the client area is meant to be done。)

【讨论】:

  • 谢谢——在最后一个小时里,我在思考这个问题时基本上得出了这些结论。如果它是已经绘制的子集,为什么要重新绘制表单? CreateGraphics 中的 Graphics 对象不知道已绘制的内容,因此它绘制了整个内容。谢谢你:)
【解决方案2】:
  1. 为什么g1在整个窗口中重新绘制图像,而g2只绘制新的部分,即使我在绘制之前调用g2.Clear()?
    我无法理解这一点,您在这两种情况下都在绘制完整的表格。所以两者都应该绘制整个表单,也许你在不同的代码中有这种行为

  2. 为什么对于任何一个 Graphics 对象,图像仅在窗口增大时才重绘,而不是在窗口变小时重绘?
    因为您应该在 Form.Resize 事件中调用 Invalidate() 以开始重绘

  3. 如果 PaintEventArgs.Graphics 可以(或不应该)用于绘图,它的用途是什么?我想如果不需要重绘表单,它只会阻止您创建新的 Graphics 实例;我还缺少更多内容吗?
    它们是不同的,CreateGraphics 是一个临时对象,应在使用后立即处理(或在 using 块中使用),它用于不需要的简单任务绘图,如MeasureString或获取Context信息。对于绘图,您始终应该使用 PaintEventArgs.Graphics。一个很好的场景是使用双缓冲时,如果使用 CreateGraphics 获取 Graphics 对象,则将获取控件的 Graphics 对象,而从 PaintEventArgs 获取它将获取缓冲位图。

【讨论】:

  • 该应用程序是默认的 Windows 窗体应用程序,仅添加了上述图像并注册了事件。没有其他代码;你可以试试看自己。感谢您对 2 和 3 的帮助;我认为我正在阅读的 MCTS 书做错了。
  • 我做到了,它在第一点的两种情况下(g1,g2)给了我相同的结果。也许这些是我们刚刚错过的。
  • 我相信您在 Form_Load 中将 DoubleBuffering 设置为 true:this.SetStyle(ControlStyles.DoubleBuffer, true) 这就是我们得到不同结果的原因..
  • DoubleBuffered 设置为 false。我没有改变任何东西,我向你保证。我的环境和你的环境肯定有一些不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-10
  • 1970-01-01
  • 1970-01-01
  • 2016-07-24
  • 2015-10-15
相关资源
最近更新 更多