当用户抓住可调整大小的窗口的角落,然后移动它时,窗口首先移动窗口的内容,然后向正在调整大小的窗口发出WM_SIZE.
因此,在我想要控制各种子控件移动的对话框中,我想消除闪烁,用户首先看到Windows操作系统认为窗口看起来像什么(因为,AFAICT,操作系统使用bitblt方法移动在发送WM_SIZE之前窗口内部的东西) - 然后我的对话框才能处理移动其子控件或调整它们等等,之后它必须强制重新绘制内容,这会导致闪烁(在此处最小).
我的主要问题是:有没有办法强迫Windows不要做这个愚蠢的bitblt事情? 在窗口调整大小时移动控件的窗口或者调整其父级调整大小时,它肯定会出错.无论哪种方式,让操作系统进行预涂,只需拧紧工件即可.
我想了一段时间它可能与CS_HREDRAW和CSVREDRAW类标志有关.然而,现实是我不希望操作系统要求我擦除窗口 - 我只是想在没有操作系统的情况下重新绘制我自己的窗口内容(即我希望显示器是它的内容)在用户开始调整大小之前 - 没有来自操作系统的任何bitblit'.而且我不希望操作系统告诉每个控件它需要重绘(除非它恰好是一个实际上被调整后显示或显示的显示.
我真正想要的是:
注意:步骤2和3可以颠倒过来.
当我将DeferSetWindowPos()与标记为WS_CLIPCHILDREN的对话框资源结合使用时,上述三件事似乎正确发生.
如果我可以将上述内容用于内存DC,那么我将获得额外的小好处,然后在WM_SIZE处理程序的末尾只执行一个bitblt.
我已经玩了一段时间了,我无法逃脱两件事:
我仍然无法抑制Windows做"预测bitblt". 答案:请参阅下面的解决方案,该解决方案将覆盖WM_NCCALCSIZE以禁用此行为.
我无法看到如何构建一个对话框,其子控件绘制到双缓冲区.答案:请参阅下面的约翰答案(标记为答案),了解如何让Windows操作系统对对话框进行双重缓冲(注意:根据文档,这不允许任何GetDC()中间的绘制操作).
我的最终解决方案(谢谢所有贡献的人,尤其是John K.):
经过大量的汗水和泪水,我发现以下技术在Aero和XP或Aero禁用时都能完美运行.轻弹不存在(1).
布局代码取决于您 - 它很容易在CodeGuru或CodeProject上找到布局管理器的示例,或者自己动手.
以下是一些代码摘录,可以帮助您完成大部分工作:
LRESULT ResizeManager::WinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_ENTERSIZEMOVE:
m_bResizeOrMove = true;
break;
case WM_NCCALCSIZE:
// The WM_NCCALCSIZE idea was given to me by John Knoeller:
// see: http://stackoverflow.com/questions/2165759/how-do-i-force-windows-not-to-redraw-anything-in-my-dialog-when-the-user-is-resiz
//
// The default …
Run Code Online (Sandbox Code Playgroud) 问题:当我抓住我的Windows应用程序的调整大小边框,特别是顶部或左边框,并调整窗口大小时,窗口的内容会在我拖动时调整"实时"大小,但是它们会以一种看似像的丑陋方式调整大小即使是最新手的用户也是一个明显的错误:窗口相对边缘的内容从边缘拖动抖动/闪烁/来回跳跃.视情况而定,现象可能如下:
一旦我停止拖动,丑陋的现象就会停止,但在拖动过程中,它会使应用程序看起来很业余和不专业.
说这个Windows问题已经让成千上万的应用程序开发人员疯狂,这并不是轻描淡写的.
以下是现象,友好地准备了两个实例图片一个相关的问题由罗马Starkov:
另一个例子展示了Kenny Liu的邪恶"双重图像"现象(请注意快速闪光):
有关任务管理器现象的另一个示例视频就在这里.
这个问题的目的: 任何遇到这个问题的开发人员都会发现至少有30个StackOverflow问题,其中一些是最近的,有些是从2008年开始的,充满了很有希望的答案很少有效.现实情况是,这一个问题有很多原因,而现有的StackOverflow问题/答案从未使更广泛的背景清晰.这个问题试图回答:
本问题的范围:对于StackOverflow问题的范围,这种现象发生在:
请勿标记为广泛或DUP:在标记为复制或标记为广泛之前,请阅读以下完整答案.我们已经经历了这个循环,SO用户在理解了以下内容之后经常重新打开这个问题:这个问题有益于社区,是第一个解释窗口调整大小抖动的所有不同原因的问答,以便用户可以识别哪个原因导致他们的问题并解决它.下面的答案很长,只是因为解释为什么一般发生抖动是很棘手的,不是因为这个问题涵盖了许多单独的问题和问题,这些错误/问题可以通过单独的问答得到更好的服务.正如您将看到的,上面的所有排列(本机/托管,窗口/对话框,XP-10)都归结为只有两个根本原因,但识别您所拥有的是棘手的部分.这就是这个问题的意义所在.将问题分开将完全否定这一好处.我们在下一节中进一步限制了问题的范围......
不在本问题的范围内:
如果您的应用程序有一个或多个子窗口(子HWND),则此问题中的信息对您很有用(因为BitBlts
我们将描述的引起混蛋的内容与父窗口一起应用于您的子窗口),但在窗口调整大小时还有另外一个问题需要处理,这超出了这个问题的范围:你需要让你的所有子窗口以原子方式移动并与父窗口同步.对于这项任务,你可能会想要 BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos
,你可以在这里和这里找到它们.
这个问题假设如果你的应用程序使用GDI,DirectX或OpenGL绘制到一个窗口,那么你已经WM_ERASEBKGND
在你的应用程序中实现了一个wndproc
简单的返回1. WM_ERASEBKGND
是来自Windows 3.1的一个神秘的Windows残余,以前WM_PAINT
给你的应用程序一个机会在你画窗户之前"擦掉窗户的背景"......嗯.如果你让WM_ERASEBKGND
消息进入DefWindowProc()
,那将导致你的整个窗口在每次重绘时被涂成纯色,通常是白色,包括在实时窗口大小调整期间发生的重绘.结果是一个丑陋的全窗口闪烁是粗略的,但不是我们在这个问题中讨论的抖动/闪烁/跳跃的类型.拦截WM_ERASEBKGND
立即解决了这个问题.
此问题主要是通过使用鼠标拖动窗口边框来实时调整大小.但是,此处编写的大部分内容也适用于当应用程序手动执行一次性窗口调整时可以看到的丑陋工件SetWindowPos()
.这些不太明显,因为它们只在屏幕上轻弹一瞬间,而不是长时间的拖动.
这个问题不是关于如何使你的特定于应用程序的绘图代码更快,即使这样做可能是许多情况下丑陋的调整大小问题的解决方案.如果您的应用确实需要花费大量时间在实时窗口调整大小期间重新显示其内容,请考虑优化绘图代码,或者至少在调整大小期间切换到更快,质量更低的绘图模式,方法是拦截WM_ENTERSIZEMOVE/WM_EXITSIZEMOVE
消息以检测调整大小.
如果您的应用程序在应用程序调整大小期间根本无法调整大小(例如,在调整大小期间它"挂起",特别是如果它是使用GLFW或其他库的OpenGL),请参阅这些其他问题,这些问题解释了WM_SYSCOMMAND
在拖动过程中Microsoft内部隐藏的嵌套/模态事件循环:这里特别是这个好的答案,这里,这里 …
我有一个使用 WTL 8.1 开发的适用于 Windows XP SP3 的 VS2008 C++ 应用程序。我的应用程序包含一个选项卡控件,该控件在调整应用程序边框大小时会闪烁。
我的窗口层次结构如下所示:
CFrameWindowImpl CMainFrm
|-CSplitterWindow Splitter
|-CTabView Configuration Tabs
| |-CDialogImpl Configuration View 1
| |-CDialogImpl Configuration View 2
| |-CDialogImpl Configuration View 3
|-CDialogImpl Control View
Run Code Online (Sandbox Code Playgroud)
我尝试的解决方案是使CFrameWindowImpl
派生类使用该WS_EX_COMPOSITED
样式,并且其下面的所有窗口都使用该WS_EX_TRANSPARENT
样式。不幸的是,这使得选项卡控件按钮显示为空黑条,并且任何配置视图的控件根本不显示。
如果我删除WS_EX_COMPOSITED
和WS_EX_TRANSPARENT
样式,表单会正确显示,但CTabView
调整大小时,其下面的所有内容都会可怕地闪烁。
我需要更改什么才能消除闪烁并正确绘制控件?
谢谢,保罗
编辑:让它工作。我WS_EX_TRANSPARENT
按照马克·兰塞姆的建议删除了所有样式。我只将样式放在(包含在)WS_EX_COMPOSITED
上。其他控件根据需要通过 获得双缓冲。CTabCtrl
CTabView
WTL::CDoubleBufferImpl<>