在调整窗口大小时是否可以完全消除闪烁?

通常,即使使用双缓冲,在调整窗口大小时,似乎也不可避免地会发生闪烁。

第1步,原始窗口。

步骤1

第2步,调整窗口大小,但额外区域尚未绘制。

第2步

步骤3,调整窗口大小,并绘制额外区域。

第3步

有可能以某种方式隐藏setp 2吗? 在绘画操作完成之前,我可以暂停resize过程吗?

这是一个例子:

#include  #include  #include  #pragma comment(lib, "Uxtheme.lib") LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL MainWindow_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct); void MainWindow_OnDestroy(HWND hWnd); void MainWindow_OnSize(HWND hWnd, UINT state, int cx, int cy); void MainWindow_OnPaint(HWND hWnd); int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wcex = { 0 }; HWND hWnd; MSG msg; BOOL ret; wcex.cbSize = sizeof(wcex); wcex.lpfnWndProc = WindowProc; wcex.hInstance = hInstance; wcex.hIcon = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED); wcex.hCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED); wcex.lpszClassName = TEXT("MainWindow"); wcex.hIconSm = wcex.hIcon; if (!RegisterClassEx(&wcex)) { return 1; } hWnd = CreateWindow(wcex.lpszClassName, TEXT("CWin32"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL); if (!hWnd) { return 1; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0) { if (ret == -1) { return 1; } TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hWnd, WM_CREATE, MainWindow_OnCreate); HANDLE_MSG(hWnd, WM_DESTROY, MainWindow_OnDestroy); HANDLE_MSG(hWnd, WM_SIZE, MainWindow_OnSize); HANDLE_MSG(hWnd, WM_PAINT, MainWindow_OnPaint); default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } } BOOL MainWindow_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { BufferedPaintInit(); return TRUE; } void MainWindow_OnDestroy(HWND hWnd) { BufferedPaintUnInit(); PostQuitMessage(0); } void MainWindow_OnSize(HWND hWnd, UINT state, int cx, int cy) { InvalidateRect(hWnd, NULL, FALSE); } void MainWindow_OnPaint(HWND hWnd) { PAINTSTRUCT ps; HPAINTBUFFER hpb; HDC hdc; BeginPaint(hWnd, &ps); hpb = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &hdc); FillRect(hdc, &ps.rcPaint, GetStockBrush(DKGRAY_BRUSH)); Sleep(320); // This simulates some slow drawing actions. EndBufferedPaint(hpb, TRUE); EndPaint(hWnd, &ps); } 

是否有可能消除闪烁?

在拖动操作期间更新窗口时,OS必须在扩展窗口区域中显示某些内容 。 如果你不能提供任何东西,它会显示背景,直到你这样做。 由于您没有指定任何背景,因此会出现黑度。 当然你应该指定一个背景画笔? 只需在代码中添加以下内容即可使行为更加可口:

 wcex.hbrBackground = GetStockBrush(DKGRAY_BRUSH); 

但是,如果您花费320毫秒来响应WM_PAINT那么您将破坏用户的resizeUI。 它变得生涩,反应迟钝。 该系统的设计基于这样的假设:您可以足够快地绘制窗口以便拖动以使其感觉平滑。 解决问题的正确方法是使WM_PAINT在合理的时间内运行。

如果你真的无法实现足够快速的绘画以实现平滑拖动,那么我建议使用以下几种方法:

  1. 拖动期间禁用窗口更新。 我确信这可以用于个别窗户,但我不记得如何做到这一点。
  2. 在resize/拖动处于活动状态时绘制一些假的东西,并推迟真实绘画,直到resize/拖动完成为止。 听WM_ENTERSIZEMOVEWM_EXITSIZEMOVE是关键。 这篇MSDN文章说明了如何执行此操作: http : //support.microsoft.com/kb/121541

使用WM_SIZING而不是WM_SIZE ,不要忘记WM_ERASEBKGND

如果进入“ 系统属性”控制面板小程序,选择“ 高级”选项卡,然后单击 性能”组框中的“设置… ”,则会在拖动时看到一个名为“ 显示窗口内容”的复选框设置。 如果取消选中并尝试调整窗口大小,您将看到只有窗口框架移动,直到您完成拖动操作,然后窗口仅以新尺寸重新绘制一次。 当我们使用缓慢,狡猾的计算机时,这就是窗口大小调整的工作原理。

现在我们真的不想全局更改设置(您可以通过使用SPI_SETDRAGFULLWINDOWS调用SystemParametersInfo来执行此操作,但实际上并不是这样,因为您的用户不喜欢它)。

当用户抓住resize边框时发生的是线程进入由窗口管理器控制的模态循环。 当循环开始时,您的窗口将获得WM_ENTERSIZEMOVE ,当操作完成时,将获得WM_EXITSIZEMOVE 。 在某些时候,你还会得到一个WM_GETMINMAXINFO,这可能与你需要做的事情无关。 当用户拖动大小调整框架时,你也会快速获得WM_SIZING , WM_SIZE消息(而快速的WM_SIZE通常会导致WM_PAINT )。

拖动设置时全局显示窗口内容负责获取快速WM_SIZE消息。 如果该设置已关闭,那么当它全部结束时,您只会收到一条WM_SIZE消息。

如果您的窗口很复杂,您可能在WM_SIZE处理程序中有布局代码计算内容(可能还有移动子窗口),并且WM_PAINT处理程序中有很多绘制代码。 如果所有代码都太慢(正如您的样本延迟320毫秒所示),那么您将获得一个闪烁的,不稳定的体验。

我们真的不想改变全局设置,但它确实激发了您的问题的解决方案:

在resize操作期间执行更简单的绘制,然后在操作结束时执行一次(较慢)复杂绘制。

解:

  1. 当您看到WM_ENTERSIZEMOVE时设置一个标志。
  2. 更改WM_SIZE处理程序以检查标志,如果已设置则不执行任何操作。
  3. 更改WM_PAINT处理程序以检查标志,如果已设置,则以纯色快速填充窗口。
  4. 当您看到WM_EXITSIZEMOVE时清除标志,然后触发布局代码并使窗口无效,以便根据最终大小更新所有内容。

如果您的慢窗口是孩子而不是应用程序的顶级窗口,则在顶级窗口获取WM_ENTERSIZEMOVE和WM_EXITSIZEMOVE时,您必须发出子窗口信号,以便实现步骤1和4。

是的你可以完全删除闪烁:)

您可以在一个线程中执行所有窗口消息处理,并在另一个线程中绘制其上下文。 您的窗口始终保持响应。 它工作得很好,无法理解为什么这不是最佳实践。

例如,如果绑定Direct3D上下文,它可以在resize时进行即时缩放,完全不需要更新上下文!

我的代码如下所示:

 int WINAPI wWinMain( HINSTANCE a_hInstance, HINSTANCE a_hPrevInstance, LPWSTR a_lpCmdLine, int a_nCmdShow ) { Win32WindowRunnable* runnableWindow=new Win32WindowRunnable(a_hInstance, a_nCmdShow); IThread* threadWindow=new Win32Thread(runnableWindow); threadWindow->start(); Scene1* scene=new Scene1(runnableWindow->waitForWindowHandle()); IThread* threadRender=new Win32Thread(scene); threadRender->start(); threadWindow->join(); threadRender->pause(); threadRender->kill(); delete runnableWindow; return 0; } 

完整的源代码示例: https : //github.com/TheWhiteAmbit/TheWhiteAmbit/blob/master/Win32App/Win32Main.cpp