当样式为 WS_CHILD 时,通过 ps.rcPaint 查找脏区不起作用
Posted
技术标签:
【中文标题】当样式为 WS_CHILD 时,通过 ps.rcPaint 查找脏区不起作用【英文标题】:Finding dirty region by ps.rcPaint not works when style is WS_CHILD 【发布时间】:2015-11-28 20:27:39 【问题描述】:我用C++写过用WinApi画的程序。 我的回调函数:
/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
switch (message) /* handle the messages */
case WM_DESTROY:
PostQuitMessage(0); /* send a WM_QUIT to the message queue */
break;
case WM_ERASEBKGND:
elWidget *widget = (elWidget *)GetWindowLong(hwnd, GWL_USERDATA);
if (widget)
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HBRUSH hBrush = CreateSolidBrush(widget->color.ColorRef());
FillRect((HDC)wParam, &ps.rcPaint, hBrush);
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
break;
default: /* for messages that we don't deal with */
return DefWindowProc(hwnd, message, wParam, lParam);
return 0;
它适用于独立窗口(样式为WS_OVERLAPPED
),但当样式为WS_CHILD
或WS_CHILD | WS_VISIBLE
时,ps.rcPaint
始终为 (0,0,0,0)。不知道怎么解决。
elButton::elButton(elWidget *owner)
: elWidget(owner)
WNDCLASSEX winclChild; /* Data structure for the windowclass */
/* The Window structure */
winclChild.hInstance = gThisInstance; //global variable instance
winclChild.lpszClassName = L"Child";
winclChild.lpfnWndProc = WindowProcedure; /* This function is called by windows */
winclChild.style = CS_DBLCLKS; /* Catch double-clicks */
winclChild.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
winclChild.hIcon = LoadIcon (NULL, IDI_APPLICATION);
winclChild.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
winclChild.hCursor = LoadCursor (NULL, IDC_ARROW);
winclChild.lpszMenuName = NULL; /* No menu */
winclChild.cbClsExtra = 0; /* No extra bytes after the window class */
winclChild.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
winclChild.hbrBackground = 0;// CreateSolidBrush(RGB(255, 200, 200));//(HBRUSH)COLOR_WINDOW;//COLOR_BACKGROUND;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx(&winclChild))
return;
hwnd = CreateWindowEx(
0, /* Extended possibilites for variation */
L"Child", /* Classname */
L"Title", /* Title Text */
WS_CHILD | WS_VISIBLE,
100,
100,
40,
40,
owner->getHwnd(), /* The window is a child-window to desktop */
NULL, /* No menu */
gThisInstance, /* Program Instance handler */
this /* to lParam */
);
SetWindowLong(hwnd, GWL_USERDATA, (long)this);
我可以在 Google Disk 上添加指向整个项目的链接,但我不能保证它会永久保存多年。
【问题讨论】:
随机猜测是您忘记设置 CS_VREDRAW | CS_HREDRAW 类样式标志。您需要显示调用 RegisterWindow/Ex() 和 CreateWindow/Ex() 的代码。 WNDCLASSEX winclChild; winclChild.style = CS_DBLCLKS if (!RegisterClassEx(&winclChild)) 返回; hwnd = CreateWindowEx(WS_CHILD | WS_VISIBLE, 显示minimal reproducible example 这样我们就不必以这种方式从您那里提取代码不是更好吗? 第一个答案:但两个窗口都是红色的;如果我们应用“unsigned style = GetWindowLong(hWnd, GWL_STYLE); if ((style & WS_CHILD) != 0) FillRect((HDC)wParam, &ps.rcPaint, hBrush);”两者都是白色的,否则是红色的 @Saku:我已经更新了我的答案。 【参考方案1】:BeginPaint 只能在 WM_PAINT 上调用,
要获取 WM_ERASEBKGND 上的剪切框,请改为调用 GetClipBox((HDC)wParam,&rect);
【讨论】:
【参考方案2】:仅将BeginPaint
/EndPaint
用于WM_PAINT
。
来自WM_PAINT
: 的 Windows 文档
应用程序不应调用
BeginPaint
,除非响应WM_PAINT
消息。对BeginPaint
的每次调用都必须有一个 对应调用EndPaint
函数。
虽然您的代码看起来可以工作,但它可能会在其他地方引起问题,例如WM_PAINT
要从WM_PAINT
外部获取更新区域,请使用GetUpdateRect
。但是,GetUpdateRect
不能用于WM_ERASEBKGND
。另见GetUpdateRect
一个简单的解决方案是强制WM_ERASEBKGND
什么都不做,然后在WM_PAINT
中处理所有事情
switch(msg)
case WM_ERASEBKGND:
return TRUE;
case WM_PAINT:
...
【讨论】:
是的,但我更喜欢只重绘脏部分;【参考方案3】:这段代码非常适合我。答案是我们必须分别为父窗口和子窗口定义单独的窗口类。我看到SetWindowLong
在所有情况下都能正常工作。但是如果你想立即用新数据重绘新窗口,你必须删除WS_VISIBLE
作为你的创建样式,并在ShowWindow
之前删除SetWindowLong
。 ShowWindow
函数使您的窗口可见并使用新参数重新绘制它。
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
int RegClass(HINSTANCE hInstance,LPCTSTR lpszClassName)
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = (LPCTSTR)lpszClassName;
wc.lpszMenuName = (LPCTSTR)NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
wc.hCursor = (HCURSOR)LoadCursor(NULL,IDC_CROSS);
wc.hIcon = (HICON)LoadIcon(NULL,IDI_APPLICATION);
return RegisterClass(&wc);
HWND hWnd0, hWnd1;
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
MSG message;
static LPCTSTR lpszClName = L"Wnd Class";
static LPCTSTR lpszCl0Name = L"Child Class";
if(!RegClass(hInstance,lpszClName))
exit(0);
if(!RegClass(hInstance,lpszCl0Name))
exit(0);
hWnd0 = CreateWindow(
lpszClName,
L"Привіт!",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
0,
0,
hInstance,
NULL
);
hWnd1 = CreateWindow(
lpszCl0Name,
L"Привіт!",
WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
40,
40,
400,
300,
hWnd0,
0,
hInstance,
NULL
);
ShowWindow(hWnd0,SW_SHOWNORMAL);
UpdateWindow(hWnd0);
RECT WndRect;
// For parent redraw
GetWindowRect(hWnd0, &WndRect);
MoveWindow(hWnd0, WndRect.left, WndRect.top, 801, 601, TRUE);
SetWindowLong(hWnd0, GWL_USERDATA, RGB(0, 255, 0));
ShowWindow(hWnd1,SW_SHOWNORMAL);
UpdateWindow(hWnd1);
while (GetMessage(&message,0,0,0))
TranslateMessage(&message);
DispatchMessage(&message);
return message.wParam;
LRESULT CALLBACK WndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
switch (message) /* handle the messages */
case WM_CREATE:
InvalidateRect(hWnd, NULL, TRUE);
break;
case WM_DESTROY:
PostQuitMessage(0); /* send a WM_QUIT to the message queue */
break;
case WM_ERASEBKGND:
if (true)
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
COLORREF color = (hWnd == hWnd0) ? RGB(255, 0, 0) : GetWindowLong(hWnd0, GWL_USERDATA);
HBRUSH hBrush = CreateSolidBrush(color);
FillRect((HDC)wParam, &ps.rcPaint, hBrush);
DeleteObject(hBrush);
EndPaint(hWnd, &ps);
ValidateRect(hWnd, NULL);
break;
default: /* for messages that we don't deal with */
return DefWindowProc(hWnd, message, wParam, lParam);
return 0;
结果会是。
【讨论】:
但要解决一个问题:类的窗口可以有不同的颜色,但无法将窗口与有颜色的对象关联起来。在 CreateWindow 或 CreateWindowEx 之前 hWnd 的值是未知的。 CreateWindow 之后已经调用了 WndProc,尤其是对于 WS_CHILD。没有 BeginUpdate 不好,但是在设置 SetWindowLong(hwnd, GWL_USERDATA, (long)this) 或放入 Map以上是关于当样式为 WS_CHILD 时,通过 ps.rcPaint 查找脏区不起作用的主要内容,如果未能解决你的问题,请参考以下文章
将 WS_CHILD 替换为 WS_POPUP 后,CWnd::CreateEx() 失败