使用 SetWindowPos 更改窗口左边缘时减少闪烁

2023-12-28

更新1:这是简化版本:

所以我有一个特殊的固定大小的子窗口,我想制作它留在右侧可调整大小的主窗口。当用户通过拖动主窗口的左/右边缘来调整主窗口的大小时,会发送 WM_WINDOWPOSCHANGED ,子窗口将在此消息处理程序中移动,使其“粘”到右侧,并且发生这种情况时不会闪烁。

但是,当我尝试通过 SetWindowPos 以编程方式调整主窗口大小时,出现明显的闪烁。似乎操作系统将旧内容复制到新客户区,甚至在我有机会处理 WM_WINDOWPOSCHANGED 中的子窗口重新定位之前。以下是 SetWindowPos 和 WM_SIZE 之间调度的消息:

WndProc: 0x00000046 WM_WINDOWPOSCHANGING
WndProc: 0x00000024 WM_GETMINMAXINFO
WndProc: 0x00000083 WM_NCCALCSIZE
WndProc: 0x00000093 WM_UAHINITMENU
===Flickering happens between these two messages!===
WndProc: 0x00000085 WM_NCPAINT
WndProc: 0x00000093 WM_UAHINITMENU
WndProc: 0x00000093 WM_UAHINITMENU
WndProc: 0x00000091 WM_UAHDRAWMENU
WndProc: 0x00000092 WM_UAHDRAWMENUITEM
WndProc: 0x00000092 WM_UAHDRAWMENUITEM
WndProc: 0x00000092 WM_UAHDRAWMENUITEM
WndProc: 0x00000014 WM_ERASEBKGND
WndProc: 0x00000047 WM_WINDOWPOSCHANGED
WndProc: 0x00000003 WM_MOVE
WndProc: 0x00000005 WM_SIZE

这是可重现的伪代码。要测试它,您可以通过 Visual Studio 的新建项目向导创建一个 Windows 桌面应用程序项目,然后将这些代码复制到适当的位置。发生闪烁是因为操作系统“BitBlt”将旧内容(由于左侧没有其他子窗口,因此为白色背景)转移到新客户区域。如果您在主窗口的左侧创建另一个子窗口,闪烁将会更加明显。

HWND g_hWndList = NULL;
#define LIST_WIDTH  500
#define LIST_HEIGHT 400

void GetListRect(HWND hWnd, RECT& rectList)
{
    GetClientRect(hWnd, &rectList);
    InflateRect(&rectList, -10, -10);
    rectList.left = rectList.right - LIST_WIDTH;
    rectList.bottom = rectList.top + LIST_HEIGHT;
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, ...);
   RECT rectList;
   GetListRect(hWnd, rectList);
   g_hWndList = CreateWindow(WC_LISTVIEW, TEXT("listR"), WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | LVS_REPORT,
       rectList.left, rectList.top, rectList.right - rectList.left, rectList.bottom - rectList.top, hWnd, nullptr, hInstance, nullptr);

   ...
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            switch (wmId)
            {
            case IDM_ABOUT:
                // Resize the window instead of showing "About" dialog
                //DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                {
                    RECT rect;
                    GetWindowRect(hWnd, &rect);
                    rect.left += 100; // make it smaller
                    SetWindowPos(hWnd, nullptr, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOZORDER);
                }
                break;
            }
        }
        break;
    case WM_WINDOWPOSCHANGED:
        {
            RECT rectList;
            GetListRect(hWnd, rectList);
            SetWindowPos(g_hWndList, nullptr, rectList.left, rectList.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
        }
        break;
    }
}

注意:当您仅使用 SetWindowPos 更改主窗口的右边缘时,不会发生闪烁。

原文内容:

假设我有一个带有两个列表控件的对话框,我希望左侧的列表控件随对话框一起调整大小,但右侧的列表控件保持相同的大小。手动调整大小时不闪烁 https://i.stack.imgur.com/PQm7Z.gif

当用户拖动对话框的左(或右)边缘来调整其大小时,不会出现闪烁。但是,当我通过调用 SetWindowPos 以编程方式执行此操作时,将会出现明显的闪烁。看来 Windows 在发送 WM_SIZE 之前就将保存的内容复制到窗口中。

SetWindowPos产生闪烁 https://i.stack.imgur.com/vVuO0.gif

我知道这个问题之前已经被提出过,有些人 https://stackoverflow.com/questions/2165759/how-do-i-force-windows-not-to-redraw-anything-in-my-dialog-when-the-user-is-resiz建议WM_NCCALCSIZE https://msdn.microsoft.com/en-us/library/windows/desktop/ms632634(v=vs.85).aspx可以帮助。尽管它的文档似乎确实是可行的方法,但我仍然无法解决闪烁问题。

代码基本上如下所示。我还放了一个演示项目 https://github.com/wingkinl/WindowResizingFlickeringDemo在 github 上。

我在这里做错了什么?

BOOL g_bExpandingShrinking = FALSE;

void OnCommandExpandShrinkWindow(HWND hWnd, BOOL bExpand)
{
    RECT rect;
    GetWindowRect(hWnd, &rect);
    rect.left += bExpand ? -100 : 100;
    UINT nFlags = SWP_NOZORDER | SWP_NOACTIVATE;
    g_bExpandingShrinking = TRUE;
    SetWindowPos(hWnd, nullptr, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, nFlags);
    g_bExpandingShrinking = FALSE;
}

LRESULT OnNcCalcSize(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    NCCALCSIZE_PARAMS* lpncsp = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
    LRESULT res;
    if (wParam && g_bExpandingShrinking)
    {
        // let DefWindowProc calculate the new client rectangle
        res = DefWindowProc(hWnd, WM_NCCALCSIZE, wParam, lParam);
        // copy the content of the right list control
        GetWindowRect(g_hwndListRight, lpncsp->rgrc + 2);
        lpncsp->rgrc[1] = lpncsp->rgrc[2];
        res = WVR_VALIDRECTS;
    }
    else
    {
        res = DefWindowProc(hWnd, WM_NCCALCSIZE, wParam, lParam);
    }
    return res;
}

窗口向左无闪烁扩展(调整大小) https://stackoverflow.com/questions/48825519/flicker-free-expansion-resize-of-a-window-to-the-left

从左侧调整大小时窗口闪烁 https://stackoverflow.com/questions/20958574/flickering-on-window-when-resizing-from-left-side


我正在与一个密切相关的问题作斗争——实时调整大小拖动窗口边框期间的闪烁问题,Windows 在内部将其实现为一组SetWindowPos() calls.

您上面提到的单个子窗口的一些闪烁可能是由于两种不同类型的 BitBlt 造成的。

第一层适用于所有 Windows 操作系统,来自BitBlt inside SetWindowPos。你可以摆脱它BitBlt在几个方面。您可以创建自己的自定义实现WM_NCCALCSIZE告诉 Windows 不传输任何内容(或者在自身之上传输一个像素),或者您可以拦截WM_WINDOWPOSCHANGING(首先将其传递给DefWindowProc)并设置WINDOWPOS.flags |= SWP_NOCOPYBITS,这会禁用BitBlt在内部调用中SetWindowPos()Windows 在调整窗口大小期间执行的操作。这具有与跳过相同的最终效果BitBlt.

然而,Windows 8/10 aero 添加了另一个更麻烦的层。应用程序现在绘制到离屏缓冲区中,然后由新的、邪恶的 DWM.exe 窗口管理器合成。事实证明 DWM.exe 有时会自己做BitBlt类型操作是在旧版 XP/Vista/7 代码已经完成的操作之上进行的。阻止 DWM 执行 blit 则要困难得多;到目前为止我还没有看到任何完整的解决方案。

将突破XP/Vista/7层并至少提升8/10层性能的示例代码请参见:

如何在调整窗口大小时平滑丑陋的抖动/闪烁/跳跃,特别是拖动左/上边框(Win 7-10;bg、bitblt 和 DWM)? https://stackoverflow.com/questions/53000291/how-to-smooth-ugly-jitter-flicker-jumping-when-resizing-windows-especially-drag

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 SetWindowPos 更改窗口左边缘时减少闪烁 的相关文章

随机推荐

  • 字典中一个键存储多个值

    我有一个数据列表 其中有 2 个值 a 12 a 11 a 5 a 12 a 11 我想使用字典 这样我就可以得到每个键的值列表 第 1 列可能有不同的条目 例如 b 所以我可以根据第1列作为键来排列数据 而第2列是每个键的数据 a 12
  • 为什么 Rails 中不推荐使用 auto_link?

    我意识到它已被拉入单独的宝石中 但原因是什么 这只是简化 Rails 的问题 还是有什么原因让我应该厌倦 auto link gem 说它的目的是 为人们迁移弥合差距 这对我来说意味着 如果我将 Rails autolink 引入到一个新项
  • iPhone 本地存储空间有限制吗?

    我想知道 iPhone 上 localstorage HTML 5 的限制是什么 我读到它大约是 5 Mb 但令我惊讶的是这么小 有任何想法吗 MobileiPhone 和 iPad 上的 Safari 在抛出错误之前将保留 5MBQUOT
  • 无法将队列添加到现有 TFS 2015 构建代理池

    升级到 TFS 2015 后尝试设置构建服务器 我设想的方式是 单代理池 将有 3 个队列 1 每晚构建 2 CI构建 3 门控 验证构建 他们每个人都会有一些代理 目标是进行一些控制 以确保夜间构建不会消耗所有代理 因此门控队列将始终有一
  • SVN合并重新整合缺失的范围但没有要合并的内容

    这是谜语 C code trunk gt svn merge reintegrate http svn e com repos branches lih accept postpone dry run svn E195016 Reinteg
  • PHP:如何查找字符串中 * 通配符的出现

    也许我问的问题有点太平庸了 但我真的不知道如何使用 PHP 检查字符串中是否出现通配符 字符 示例字符串 bcd OR ab d OR abc 无论我尝试使用什么 PHP 函数 它的行为都是不可预测的 我只需要知道通配符是否在字符串中 非常
  • 从 AsyncTask 管理 ProgressDialog 的最佳方法

    我想使用AsyncTask用于管理我的应用程序中的一些业务逻辑 使用的最佳模式是什么onProgressUpdate 的方法AsyncTask在单独的文件中定义 不是作为内部类Activity 我有两个想法 1 最简单的方式 创建Progr
  • 保护控制器除一个(登录)之外的所有操作的最佳方法是什么?

    目前我有 Authorize 我的所有方法的属性AdminController除了Logon action 什么是cleanest反转这个的方法 所以我不必记住向所有方法添加属性 而是仅向无需登录即可使用的方法添加属性 我会更好地移动Log
  • 为什么我在使用 MockMvc 和 JUnit 时收到错误 403?

    我有一个带有 spring security 3 2 的 spring mvc 3 2 5 应用程序 我用这个方法配置了我的 SecurityConfig class Override protected void configure Ht
  • 笔记本电脑触控板的 WM_INPUT 上的 RAWINPUTHEADER hDevice 为 null

    我使用原始输入来处理通用设备的输入 到目前为止 我的所有测试用例都有效 键盘 游戏板和鼠标 但我的笔记本电脑触控板给我带来了一个奇怪的问题 当我得到一个WM INPUT https learn microsoft com en us win
  • 创建 Windows 服务的最简单语言

    构建 Windows 服务最简单的语言是什么 在这种情况下 最简单的定义是最少的代码量和最低的语言入口点 冒着陈述显而易见的风险 如果您有任何 C C Java 背景 我认为 C 为您提供了最低的切入点 假设您使用的是 Visual Stu
  • 在构造函数中注册weak_ptr观察者

    我正在尝试重写我们的 Observer Observable 实现 以使用 std shared ptr std weak ptr 来消除代码中当前存在的一些令人讨厌的竞争条件 通常 观察者在满足某些条件或构造子对象时注册自己 如下所示 U
  • 从给定字符串中查找按字母顺序排列的最长子字符串

    我一直在研究一个问题 从给定的字符串中按字母顺序查找最长的子字符串 我在 C 方面有很多经验 但对 python 完全陌生 我写了这段代码 s raw input Enter a sentence a 0 start int b 0 end
  • 使用 jquery/ajax 更新 django 表单

    我想更新下拉列表的更改事件的表单 这是我的观点 from django utils import simplejson response dic drop down list actToValidateId add form cleaned
  • Symfony2 QueryBuilder 中带有计数和分组依据的 SQL 查询

    我需要你的帮助 我有这个 SQL 查询 SELECT COUNT AS count FROM mytable GROUP BY email ORDER BY id DESC LIMIT 0 30 但我想在 Symfony2 中使用 Doct
  • Dagger 2:如何注入Map, Provider>

    在 Dagger 2 中 是否可以注入Map
  • Jenkins 中的多个构建队列

    我在 Jenkins 中有许多不同的工作 分为具有自己的构建 测试 分析 管道 的项目 大多数这些工作实际上是远程命令而不是机上构建 然而 开箱即用的 Jenkins 仅支持一个队列all构建 我想为每个项目 或视图 定义一个队列 我将如何
  • 手势监听器 onFling 的行为不一致

    更新 请参阅赏金以了解扩展问题 我有一个GestureDetector设置在ListView ListView 是一个完整的片段 来自窗口的一侧并部分覆盖另一个片段 我想让用户能够将其关闭 即奇妙清单是右侧此功能的一个很好的例子 这是我的设
  • Android Studio 3.0 中的资源 ID 未更新

    我最近更新到 Android Studio 3 0 并注意到当我添加任何 android id 或创建新的可绘制对象或基本上任何资源时 Android Studio 不会在项目中更新它 直到我构建应用程序 我在 xml 中创建的每个资源在类
  • 使用 SetWindowPos 更改窗口左边缘时减少闪烁

    更新1 这是简化版本 所以我有一个特殊的固定大小的子窗口 我想制作它留在右侧可调整大小的主窗口 当用户通过拖动主窗口的左 右边缘来调整主窗口的大小时 会发送 WM WINDOWPOSCHANGED 子窗口将在此消息处理程序中移动 使其 粘