5

好的,所以在我的应用程序中,有一堆 winAPI 和一些自定义控件。耶...

现在,通常情况下,他们会悄悄地为动画、状态改变等重新绘制自己……一切都很好。

但我有一个名为fix() 的类Window 方法。每当需要更新整个窗口时都会调用它。它调整控件的大小并使窗口无效。

发生这种情况时,将绘制背景,然后是选项卡控件,然后是顶部的所有其他控件。这会导致非常恼人的闪烁,尤其是在调整窗口大小时(因为不断调用 fix())。

我试过的:

  • WS_EX_COMPOSITED。这只会对各个控件进行双重缓冲。它是一种改进,但闪烁不可避免地仍然存在。
  • 关闭背景绘图。很难解决问题,实际上使事情变得更糟。

所以:我需要一种技术/方法/任何东西来让我对整个窗口进行双重缓冲。我认为自己处理 WM_PAINT 消息可能是一种解决方案,但我不知道从哪里开始。我有一种可怕的感觉,这甚至是不可能的……

请帮忙,这是一个关键问题。当这个愚蠢的小问题得到解决时,我会感到非常欣慰。

4

5 回答 5

6

正是在这个时候,人们才意识到微软对原生开发者漠视的深度。事实上,人们可能会开始怀有一种偏执的错觉,即微软故意破坏原生绘画以迫使原生开发人员转向 WPF。

首先,考虑WS_EX_COMPOSITED. WS_EX_COMPOSITED似乎是芥末:- 它说它强制一个 botton to top 子控件的绘制顺序,并且基本上 WM_PAINT 消息是批量处理的。它说它是在 Windows 2000 (5.0) 中添加的,并且在几行之后,它不适用于启用桌面合成。即它从 Windows Vista (6.0) 起停止工作,除非航空玻璃已关闭,谁来执行此操作?

然后,有两种可能的“技巧”来尝试让无闪烁绘画工作:

  • 首先,您需要最小化过度绘制的数量。WS_EX_CLIPCHILDREN | WS_EX_CLIPSIBLINGS有必要确保窗口的任何特定区域只绘制一次。BeginDeferWindowPos还需要批量调整大小操作,以确保不会发生瞬态(一个窗口与另一个窗口重叠的情况)(即,当窗口 A 已调整大小但窗口 B 未调整大小时)。

当然,当您尝试绘制蒙皮对话框、使用组框、选项卡控件或任何其他限制时,WS_EX_CLIPSIBLINGS都是不合适的。

  • WM_SETREDRAW是一个神奇的信息。没有 API 可以访问此功能:WM_SETREDRAW由 DefWindowProc 直接处理,本质上将窗口标记为在持续时间内隐藏。发送后WM_SETREDRAW, FALSE,使用父窗口句柄(及其所有子窗口句柄)调用 GetDC/GetDCEx/GetWindowDC 等将返回一个不会在屏幕上绘制的 DC。这使您有机会对子窗口执行各种操作,完成后发送一个WM_SETREDRAW,TRUE, (然后手动重新绘制窗口)。当然,所有子窗口都会在自己的时间进行绘制,并且在父窗口完成擦除背景之后,因此 WM_SETREDRAW 不是灵丹妙药。

破解后WS_EX_COMPOSITED,在 .NET 的 WinForms 和 WPF 中从头开始重新编写控件以不使用本机控件,因此它们可以在那里烘焙缓冲绘画。还有 alpha 支持。

于 2010-10-11T10:35:39.693 回答
1

嗯。在人们急于将这个话题作为一个副本关闭之前,我最好提一下,你的问题不是双缓冲本身,而是在重新定位控件时闪烁,“手动”双缓冲只是几种解决方案之一。

可能是例如BeginDeferWindowPos和朋友可以修复闪烁。

免责声明:我曾经几乎知道 Win16 API 的所有细节,但我已经有好几年没有做 API 级别的编程了。

干杯&hth.,

– 阿尔夫

于 2010-10-11T07:07:24.313 回答
0

没有代码我很难想象实际情况是什么......但是你可以尝试处理 WM_ERASEBKGND,只要你收到它们就返回 TRUE 以跳过擦除。

于 2010-10-11T07:06:53.930 回答
0

处理WM_ERASEBKGND您的窗口并在实现中排除每个子控件的矩形,然后适当地填充剩余的背景并通过返回告诉框架您已经自己绘制了背景true。对所有其他子控件执行相同的操作,这些子控件在您的情况下又包含其他控件,例如选项卡控件。

有关使用 MFC 的示例,请参见此处。

于 2010-10-11T07:07:34.500 回答
0

在 .net 中,ControlStyle.AllPaintingInWmPaint应该在每个容器窗口(所以主窗口和选项卡)上设置。我找不到等效的 winapi。但这所做的是将背景的绘画从 WM_ERASEBKGND 移动到 WM_PAINT。它还会从父控件中减去子控件的区域以避免闪烁,这可能是您需要手动执行此操作。

我很惊讶 WS_EX_COMPOSITED 没有帮助。如果您在顶级窗口上设置它,并且不要在子控件上调用 RedrawWindow,那应该可以工作。

还要确保在有和没有 DWM/Aero 的情况下进行测试。你可以得到不同的结果。

于 2010-10-11T07:10:49.250 回答