【问题标题】:BeginPaint() not erasing the backgroundBeginPaint() 不擦除背景
【发布时间】:2015-11-13 18:15:10
【问题描述】:

BeginPaint() 文档说:

如果窗口的类有背景画笔,BeginPaint 会使用它 在返回之前刷掉更新区域的背景。

我已经这样设置画笔了:

wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);

我在我的 Window 上绘制了一个位图,然后我要求使用以下代码重绘:

RedrawWindow(hWnd, 0, 0, RDW_INVALIDATE | RDW_UPDATENOW);

我在WM_PAINT 中有此代码:

case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);

        EndPaint(hWnd, &ps);
    }
    break;

所以我预计BeginPaint() 会擦除位图,但位图仍保留在窗口上!

我使用的是 Windows 7。

【问题讨论】:

  • 您需要添加RDW_ERASE 标志才能清除背景。
  • @Jonathan Potter 它有效,但现在是 RedrawWindow() 正在擦除背景,而不是 BeginPaint()
  • 不,不是。 RedrawWindow 将该区域标记为需要擦除。
  • @Jonathan Potter 但是我删除了WM_PAINT里面的代码,背景也被抹掉了。
  • 如果您不处理 WM_ERASEBKGND 并让它通过 DefWindowProc 擦除将在那里完成。

标签: c winapi gdi


【解决方案1】:

(将其写为答案,因为解释太长,无法评论)。

在标准 Windows 模型中可以在两个地方绘制客户区:

WM_ERASEBKGND 名义上处理擦除背景。 WM_PAINT 名义上处理在背景上绘制东西。

如果您将WM_ERASEBKGND 传递给DefWindowProc,它将使用类画笔绘制背景。如果您想自己处理WM_ERASEBKGND,您有两种选择:

  • 您可以自己擦除背景,并返回 1(或任何非零值)
  • 可以不擦除,返回0

如果你实现后一个选项,当你稍后收到WM_PAINT消息并调用BeginPaint时,PAINTSTRUCTfErase标志将设置为TRUE,表示背景仍然被标记用于擦除。

BeginPaint 函数本身根本不做任何绘画,它所做的只是给你一个DC,你可以在其中绘画。当您调用EndPaint 时,之前标记为脏的区域被“视为”已被绘制(和擦除)并且将不再被标记为脏。

背景擦除和前景绘画中的绘画划分(至少在我看来)是 16 位世界的残余,绘画是一项昂贵的操作。如果前景绘画也擦除了背景(例如,您正在绘制纯色块或纯色图像),那么将背景作为单独的步骤擦除是浪费时间,并且通常会导致可见的闪烁。因此,根据您的应用程序,您可能希望完全忽略“擦除背景”步骤 - 从 WM_ERASEBKGND 返回 0 并在 WM_PAINT 中一步绘制“背景和前景”。

【讨论】:

  • 很好的解释。我对bg擦除和绘画划分的理论是有用的,因为WM_PAINT是惰性的并且通常是异步的,但WM_ERASEBKGND是同步的。也就是说,当您使窗口无效时,立即发送 WM_ERASEBKGND,但延迟 WM_PAINT 直到空闲。这样,用户将立即获得视觉反馈,并不再看到旧的、可能令人困惑的数据。正如你所说,缺点是闪烁。
  • @rodrigo 我实际上感觉WM_ERASEBKGND 本身通常是由对BeginPaint 的调用生成的,尽管我现在在任何地方都找不到对它的引用。另请注意,如果您将WM_PAINT 传递给DefWindowProc,那么它将为您调用BeginPaint/EndPaint,因此这可能会解释您书中的评论(如果我是对的,那就是BeginPaint负责发送WM_ERASEBKGND)。
  • (例外情况是带有RDW_ERASE | RDW_ERASENOW 标志的RedrawWindow,它可能会在内部发送WM_ERASEBKGND)。
  • “我实际上有一种感觉,WM_ERASEBKGND 本身通常是由对 BeginPaint 的调用生成的,尽管我现在在任何地方都找不到对它的引用。” 可能是这个不是找不到参考的意思,而是BeginPaint() 文档说:“BeginPaint 向窗口发送 WM_ERASEBKGND 消息”
  • 我认为你错了,WM_ERASEBKGND 是在InvalidateRect() 时生成(发送)的。您可以使用调试器轻松检查它(我记得很久以前就这样做过)。当然RedrawWindow()是不同的,但是通常的重绘周期应该是invalidate-erase-paint。
猜你喜欢
  • 2015-05-08
  • 1970-01-01
  • 2020-09-02
  • 2011-07-09
  • 2010-10-01
  • 2016-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多