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

EFa*_*nZh 9 c winapi double-buffering

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

第1步,原始窗口.

步骤1

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

第2步

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

第3步

有可能以某种方式隐藏setp 2吗?我可以暂停调整大小过程直到绘画操作完成吗?

这是一个例子:

#include <Windows.h>
#include <windowsx.h>
#include <Uxtheme.h>

#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);
}
Run Code Online (Sandbox Code Playgroud)

是否有可能消除闪烁?

Dav*_*nan 8

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

wcex.hbrBackground = GetStockBrush(DKGRAY_BRUSH);
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 一个很大的+1.并且,对于任何WM_PAINT来说,320毫秒都是不可接受的.这是一个真正的问题.如果是我,我将异步组合图像并仅在WM_PAINT上渲染预先组合的位图.在图像合成期间,系统会免费为您提供背景画笔,然后您可以显示先前合成图像的"StretchBlt"图像,以便在320毫秒内为用户提供一些东西.取决于你的窗口的内容,这是一个好主意还是一个可怕的. (4认同)