大量重绘窗口时,如何避免 Win32 控件闪烁?

-1

我的一个 Win32 程序运行时需要大量重绘,但这样会导致它的 Win32 控件闪烁。

此外,我还通过在窗口过程函数中处理 WM_CTLCOLORSTATIC 消息,使我的控件背景透明了:

case WM_CTLCOLORSTATIC:
{
	HDC hdc = (HDC)wParam;
	SetBkMode(hdc, TRANSPARENT);
	SetTextColor(hdc, RGB(255, 0, 0));
	return (LRESULT)GetStockObject(NULL_BRUSH); // 控件背景透明
}

为了解决控件闪烁的问题,我已经尝试了在主窗口中启用 WS_CLIPCHILDREN 属性,但是它会导致我的控件背景不再透明。

请问怎样能在窗口大量重绘的情况下,使我的控件不闪烁,而且背景是透明的?

为了说明 WS_CLIPCHILDREN 属性会导致控件背景不再透明,以下附了一份代码。代码中创建了一个窗口和一个透明背景的 Static 控件,但是由于启用了 WS_CLIPCHILDREN 属性,控件背景变得不透明了。(这个代码用的是 main 函数,而不是 WinMain,需要控制台项目编译)

#include <windows.h>
#include <thread>

#define IDC_STATIC 100

//////////// 设置是否启用 WS_CLIPCHILDREN 样式
//////////// 启用时可以避免闪烁,但是控件背景不透明了
bool g_bEnableStyle = true;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	static HINSTANCE hInstance = GetModuleHandle(0);
	static HWND hStatic;

	switch (msg)
	{
	case WM_CREATE:
	{
		hStatic = CreateWindow(
			L"static",
			L"我是透明控件",
			WS_CHILD | WS_VISIBLE,
			20, 22, 100, 20,
			hwnd,
			(HMENU)IDC_STATIC,
			hInstance,
			NULL
		);

		hStatic = CreateWindow(
			L"button",
			L"BTN",
			WS_CHILD | WS_VISIBLE,
			220, 22, 100, 20,
			hwnd,
			(HMENU)IDC_STATIC,
			hInstance,
			NULL
		);

		break;
	}

	case WM_CTLCOLORSTATIC:
	{
		HDC hdc = (HDC)wParam;

		SetBkMode(hdc, TRANSPARENT);
		SetTextColor(hdc, RGB(255, 0, 0));

		return (LRESULT)GetStockObject(NULL_BRUSH);
	}

	case WM_CLOSE:
		DestroyWindow(hwnd);
		PostQuitMessage(NULL);
		exit(0);
		break;
	}

	return DefWindowProc(hwnd, msg, wParam, lParam);
}

void initwindow(int w, int h, int mode, LRESULT(_stdcall* WindowProcess)(HWND, UINT, WPARAM, LPARAM), LPCTSTR strWndTitle)
{
	// 窗口类
	WNDCLASSEX WndClassEx;
	MSG Msg;

	// 隐藏cmd
	if (mode == 0)
		ShowWindow(GetConsoleWindow(), SW_HIDE);

	// 填写结构体
	WndClassEx.cbSize = sizeof(WNDCLASSEX);
	WndClassEx.style = CS_VREDRAW | CS_HREDRAW;
	WndClassEx.lpfnWndProc = WindowProcess;
	WndClassEx.cbClsExtra = 0;
	WndClassEx.cbWndExtra = 0;
	WndClassEx.hInstance = GetModuleHandle(0);
	WndClassEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	WndClassEx.hCursor = LoadCursor(NULL, IDC_ARROW);
	WndClassEx.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	WndClassEx.lpszMenuName = NULL;
	WndClassEx.lpszClassName = strWndTitle;
	WndClassEx.hIconSm = NULL;

	// 注册窗口类
	RegisterClassEx(&WndClassEx);

	// 创建窗口
	HWND hWnd = CreateWindowEx(
		WS_EX_WINDOWEDGE,
		strWndTitle,
		strWndTitle,
		WS_OVERLAPPEDWINDOW | (g_bEnableStyle ? WS_CLIPCHILDREN : 0),
		200, 200, w, h,
		NULL,
		NULL,
		GetModuleHandle(0),
		NULL
	);

	// 显示窗口
	ShowWindow(hWnd, SW_SHOWNORMAL);

	// 更新窗口
	UpdateWindow(hWnd);

	clock_t time = clock();

	// 循环获得消息
	while (GetMessage(&Msg, NULL, NULL, NULL))
	{
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);

		// 模拟大量重绘
		if (clock() - time >= 10)
		{
			InvalidateRect(hWnd, nullptr, true);
			time = clock();
		}
	}
}

int main()
{
	initwindow(640, 480, 0, WndProc, L"mywnd");
	return 0;
}
ava
huidong

2022-12-1

0

根据msdn上的说法, 父窗口设置WS_CLIPCHILDREN属性后 在父窗口中绘制时,排除子窗口占用的区域。 也就是说在你调用updatewindow重绘父窗口时, 子窗口所在区域已经被裁剪了, 所以并不会绘制子窗口所在的位置, 所以并不是透明失效, 而是父窗口这里并没有绘制

ava
喵爪

2022-12-9

抱歉,一直在学校没看到回复。如果不使用 WS_CLIPCHILDREN,虽然可以得到控件的透明效果,但是会导致频繁重绘窗口时控件闪烁,这怎么解决呢? -  huidong  2022-12-17
技术讨论社区