是否有可能消除调整窗口大小时闪烁完全?(Is it possible to eliminate fl

2019-07-31 05:03发布

通常情况下,使用双缓冲,即使,调整窗口大小时,它似乎是不可避免的闪烁会发生。

步骤1中,原来的窗口。

第2步,调整窗口大小,但多余的面积并没有被涂。

第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);
}

是否有可能消除闪烁?

Answer 1:

当拖动操作过程中更新窗口,那么操作系统必须显示了扩展窗口区域的东西 。 如果你不能提供什么,然后它会显示背景,直到你怎么做。 既然你没有指定任何背景你的黑暗。 当然,你应该是指定的背景刷? 只需添加以下到您的代码的行为更可口:

wcex.hbrBackground = GetStockBrush(DKGRAY_BRUSH);

不过,如果你需要长达320ms来的回应WM_PAINT那么你毁了用户调整大小UI。 它变干,反应迟钝。 该系统的设计是围绕你可以绘制窗口更快,以适应拖到手感光滑的假设。 解决您的问题的正确方法是使WM_PAINT运行在一个合理的时间。

如果你真的无法实现平滑拖动速度不够快画那么我建议几个选择:

  1. 拖动过程中禁用窗口更新。 我敢肯定,这可以为各个窗口来完成,但我不记得如何做到这一点从我的头顶。
  2. 涂料假的东西,同时调整大小/阻力有效,并且推迟了真正的绘画,当调整大小/阻力已完成,直到。 监听WM_ENTERSIZEMOVEWM_EXITSIZEMOVE的关键是这个。 这MSDN文章说明了如何做到这一点: http://support.microsoft.com/kb/121541


Answer 2:

使用WM_SIZING代替WM_SIZE不要忘记WM_ERASEBKGND



Answer 3:

如果你进入系统属性控制面板程序,选择高级选项卡,然后单击设置......表演团体中,你会看到一个复选框,设置所谓的显示窗口内容的同时拖动 。 如果取消选中,并尝试调整窗口大小,你会看到只有窗框移动,直到你完成拖动操作,然后在窗口的新的大小重新绘制一次。 这是窗口大小如何使用时,我们不得不慢,这些混沌电脑工作。

现在,我们真的不希望在全球范围内更改设置(你会通过调用做SystemParametersInfo与SPI_SETDRAGFULLWINDOWS,但真的不这样做,因为你的用户不会喜欢它)。

当用户抓住调整边框大小是线程进入窗口管理器控制的模式循环会发生什么。 你的窗口将得到WM_ENTERSIZEMOVE作为循环开始和WM_EXITSIZEMOVE当操作完成。 在某些时候,你还可以得到一个WM_GETMINMAXINFO,这可能是不相关的,你需要做什么。 您还可以得到WM_SIZING , WM_SIZE消息迅速为用户拖动尺寸框架(和快速WM_SIZEs往往会导致WM_PAINT S)。

全球显示窗口内容的同时拖动设置负责获取快速WM_SIZE消息。 如果设置为关闭,你只得到一个WM_SIZE消息,当这一切都结束了。

如果你的窗口是复杂的,你可能有在WM_SIZE处理布局代码计算的东西(也许移动子窗口),并在WM_PAINT处理大量的绘制代码。 如果所有的代码太慢(如您的样品320毫秒的延迟提示),那么你将有一个flickery,生涩的经验。

我们真的不希望改变全局设置,但它确实激发出解决问题的方法:

大小调整操作过程中做简单的拉伸,然后做你的(慢)复杂的图纸只有一次,当操作结束。

解:

  1. 当你看到WM_ENTERSIZEMOVE设置一个标志。
  2. 改变你的WM_SIZE处理程序来检查该标志,如果它的设置做任何事。
  3. 改变你的WM_PAINT处理程序来检查该标志并执行窗口的简单,快速填充的纯色,如果它的设置。
  4. 清除标志,当你看到WM_EXITSIZEMOVE,然后触发布局代码等一切系统会根据最终的大小来更新您的无效窗口。

如果你慢窗口是一个孩子,而不是应用程序的顶层窗口,你必须在顶层窗口获取WM_ENTERSIZEMOVE和WM_EXITSIZEMOVE为了实现步骤1和4的信号的子窗口。



Answer 4:

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

你可以做所有的窗口消息处理一个线程,并画它在另一个环境。 你的窗口始终保持响应。 它的伟大工程,不明白为什么这是不成立的最佳实践。

如果绑定例如Direct3D的背景下,可以在缩放,完全不必上下文更新有一个即时缩放!

我的代码如下所示:

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



文章来源: Is it possible to eliminate flickering entirely when resizing a window?