【发布时间】:2018-06-06 13:22:28
【问题描述】:
据我了解,Windows 对给定窗口的(重新)绘制进行了分工;分为背景擦除和前景绘画。发送WM_ERASEBKGNDmessage 是为了准备给定窗口的无效部分进行绘画,通常这种准备包括擦除背景,以便可以在干净的画布上开始实际绘画。在我看来,这条消息总是在 Windows 使给定窗口的一部分无效时发送(因此基本上总是与正在发布的 WM_PAINT 消息一起发送)。每当应用程序本身使给定窗口(部分)无效时,InvalidateRect 函数的最后一个参数指定是否发送WM_ERASEBKGND。所以我写了一个小程序来测试我的假设,但它的行为让我有点难以理解。这是说程序:
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hwInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Run the message loop.
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static int eb_count = 0; // counts number of WM_ERASEBKGND messages
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect;
wchar_t text[40];
wsprintf(text, L"Number of WM_ERASEBKGND messages: %i\n", eb_count);
GetClientRect(hwnd, &rect);
DrawText(hdc, text, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
case WM_RBUTTONDOWN: // repaint whenever RBUTTONDOWN
InvalidateRect(hwnd, NULL, FALSE);
UpdateWindow(hwnd);
return 0;
case WM_ERASEBKGND:
eb_count++;
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
我在窗口过程中处理WM_ERASEBKGND(这是我的开关中的一个案例),所以它不应该传递给默认窗口过程。但是,我没有进行任何实际的背景擦除(我只是增加一个静态变量)并且我返回 0 表示实际上没有发生擦除。在我看来,在这个程序中,背景永远不应该被抹去。然而,这确实发生在两个不同的情况下。
每当我最大化窗口时,无效部分的背景会被类背景画笔擦除。但这怎么可能?窗口过程在收到WM_ERASEBKGND 消息时肯定不会做这样的事情。
每当DrawText 重新绘制其字符串时,都会发生类似的事情。我希望递增的数字会被绘制在彼此之上(当然会导致难以辨认的混乱)。因此,“DrawText”函数似乎也以某种方式擦除了它绘制字符串的矩形的背景。
我的最后一个问题与我的假设有关,即只要 Windows 使窗口的一部分无效,就会发送 WM_ERASEBKGND 消息。我注意到,每当窗口被另一个窗口覆盖并随后被覆盖时,似乎都没有发送WM_ERASEBKGND 消息。这是否意味着我的假设是错误的?
很抱歉读了这么久,但在回答这些问题方面的任何和所有帮助将不胜感激。
【问题讨论】:
-
查看
WM_ERASEBKGND的文档 - 您没有擦除背景,并且您返回 0 以响应WM_ERASEBKGND。也许你想要case WM_ERASEBKGND:break;或者干脆不说。或者在WM_PAINT做背景画 -
如果你想阻止默认背景绘制,在处理
WM_ERASEBKGND的代码中什么都不做,并返回一个非零值告诉Windows你已经处理了它。在这种情况下,您应该在WM_PAINT中完成所有绘画(包括背景绘画)。为防止DrawText()清除后台,请先调用SetBkMode(TRANSPARENT)。 -
@zett42 返回 0 还是非零值真的很重要吗?据我所知,唯一真正的区别是,当我返回 0 时,
PAINTSTRUCT的fErase标志将设置为TRUE。如果我返回一个非零值,该标志将设置为FALSE。除此之外,没有真正的区别。在任何一种情况下,真正重要的是在WM_ERASEBKGND期间实际发生的事情,这在我的程序中什么都不是。所以无论我返回什么,无论哪种情况我都可以处理WM_PAINT中的所有绘画,对吧? -
reference 并不是说
PAINTSTRUCT::fErase标志是唯一受WM_ERASEBKGND的返回值影响的东西。注意“通常”这个词。所以我会安全地玩它并且总是返回TRUE。
标签: winapi background erase