25

我对 Delphi 2009/2010 对 Windows 中 Aero Theme Glass 功能的支持、DoubleBuffered 的确切含义以及它与 Aero glass 的关系感到困惑。我发现 DoubleBuffered 不仅是 VCL 中的一个属性,它也可以在.net WinForms中找到。最初我想知道它是否设置了公共控件库使用的某种窗口样式位,或者什么。为什么使用它,何时使用?

[更新:我应该声明我知道什么是“双缓冲”,作为一种减少闪烁的通用技术,我想知道的是,为什么它与 Windows Vista 中 Aero Glass 窗格上的渲染控件有任何关系/ Windows 7,特别是为什么所有事物的 BUTTON 都需要将双缓冲设置为 true,才能在玻璃上工作?下面链接的博客文章似乎信息量最大。]

尤其是对DoubleBuffered属性很困惑,想知道它为什么存在,它的玻璃支架和窗体、控件中的双缓冲属性是什么关系。当您阅读像这样的 C++ 文章时,您会发现没有提到双缓冲。

[更新2:以下内容包含一些事实错误,并已进行修改:]

我发现一些 C++ 开发人员谈论他们如何调用 SetLayeredWindowAttributes 以避免在经典 Win32 应用程序中打开 DWM/Aero 合成导致的“黑色变成玻璃”故障 [但是下面的博客链接告诉我这不再有效在 Windows 7 中,实际上只是在 Vista 中短暂工作,直到微软阻止它]。[Begin WRONG Idea] 我们不应该使用其他颜色,比如明亮的洋红色,然后把它变成玻璃透明色吗?[结束错误的想法]

什么时候应该设置和不设置 DoubleBuffered 的规则是什么,为什么首先将 DoubleBuffered 添加到 VCL 中?什么时候设置会出问题?(似乎远程桌面是一种情况,但这是唯一的情况吗?)当它没有设置时,我们会在按钮文本的渲染中出现故障,很可能是因为 Delphi 似乎没有更改默认的“将黑色渲染为玻璃“在 Aero DWM 中。

在我看来,Aero Glass 渲染从根本上是以一种奇怪或难以理解的方式完成的[由 Windows 本身,而不是仅包装此功能的 Delphi],并且 2009/2010 年的许多内部 VCL 源代码StdCtrls 中的类必须做很多复杂的逻辑才能在 Aero Glass 上正确渲染东西,但它仍然有很多问题,在我看来它做错了,这可能是这个相关问题和 qc 问题的背后。 [更新 3:玻璃上的许多渲染故障,在 VCL 中,在通用控件中渲染完成错误,微软似乎并不关心修复。简而言之,Delphi VCL 代码修复无法解决这样一个事实,即古老的 Windows 通用控件库和现代 [但古怪的] Aero Glass 合成功能彼此不太喜欢,并且不能很好地协同工作。感谢 Microsoft 构建了如此高质量的技术并将其释放到世界各地。]

如果还不够有趣;为什么我们有 ParentDoubleBuffered?

[7 月 30 日更新:这个问题对我来说很有趣,因为我认为它表明,当你有一个大型的现有 VCL 框架时,使用 Windows API 来解决这个问题是一个难题。]

4

4 回答 4

15

关于双缓冲

.NET 可能有一个,它可能与 Delphi 的名称和用途相同,但 Delphi 正在从头开始实现 DoubleBuffer,我假设 .NET 也是如此。没有使用窗口样式位来实现这一点。

双缓冲和玻璃航空

相当简单:不要为 Glass 上的控件设置 DoubleBuffer。为了使 DoubleBuffering 工作,必须能够初始化“缓冲区”——但是为 Glass 初始化它需要什么?Windows 标准控件(包括 TButton)不需要 DoubleBuffering。对于需要透明表面和类似双缓冲行为的新控件,可以使用分层窗口 API。

让控件在 Glass 上工作

第1步:

TForm1 = class(TForm)
...
protected
  procedure CreateWindowHandle(const Params: TCreateParams); override;
...
end;

procedure TForm15.CreateWindowHandle(const Params: TCreateParams);
begin
  inherited;
  SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_LAYERED);
  SetLayeredWindowAttributes(Handle, RGB(60, 60, 60), 0, LWA_COLORKEY);
end;

第 2 步,这应该是您的表单的 OnPaint 处理程序:

procedure TForm15.FormPaint(Sender: TObject);
var rClientRect:TRect;
begin
  if GlassFrame.Enabled then
  begin
    rClientRect := ClientRect;

    Canvas.Brush.Color := RGB(60, 60, 60);
    Canvas.Brush.Style := bsSolid;
    Canvas.FillRect(rClientRect);

    if not GlassFrame.SheetOfGlass then
    begin
      rClientRect.Top := rClientRect.Top + GlassFrame.Top;
      rClientRect.Left := rClientRect.Left + GlassFrame.Left;
      rClientRect.Right := rClientRect.Right - GlassFrame.Right;
      rClientRect.Bottom := rClientRect.Bottom - GlassFrame.Bottom;
      Canvas.Brush.Color := clBtnFace;
      Canvas.FillRect(rClientRect);
    end;
  end;
end;

第 3 步:设置 GlassFrame.Enabled = True; 设置所有其他 Glass 属性,将控件添加到表单中,无论您喜欢它们。可能在 Glass 或其他任何地方。确保控件没有“DoubleBuffered = True”。就是这样,享受。我已经用 TButton、TCkBox 和 TEdit 进行了测试。

... 编辑 ...

不幸的是,使用这种方法“玻璃”被视为 100% 透明的表面,它不是 - 它看起来像玻璃,但它的行为不像玻璃。100% 透明度的问题是,如果您点击该透明区域,您的点击会转到窗口后面的窗口。可怕。

在撰写本文时,我很确定没有 API 可以更改原始玻璃的默认 BLACK 键颜色(谷歌发现了无数博客和论坛帖子,说明您需要如何为玻璃上的控件使用自定义绘图并且没有在 MSDN 上的 DWM 函数列表中更改它的函数)。在不更改默认黑色的情况下,大多数控件无法正确呈现,因为它们使用 clWindowText 编写文本,而这就是黑色。在几个论坛上发现的一个建议技巧是使用 SetLayeredWindowAttributes API 更改透明度颜色。它有效!一旦完成,控件上的黑色文本就会显示抛出,但不幸的是玻璃不再是玻璃,玻璃看起来像玻璃,但表现得像 100% 透明度。这几乎使该解决方案无效,并显示了 Microsoft 方面的双重标准:最初的 BLACK 的行为不像 100% 透明,但如果我们将其更改为更好的东西,它的行为就像 100% 透明。

在我看来,在 Glass 上使用自定义控件的普遍想法是错误的。这是唯一可行的方法,但它是错误的,因为我们应该使用跨平台一致的控件:建议自定义控件为不一致的、类似 winamp 的应用程序打开了大门,每个用户重新创建轮子适合它的艺术理念。即使开发人员设法忠实地重新创建任何给定的窗口控件并使其在玻璃上工作,“修复”也只是暂时的,需要为下一个版本的窗口重新创建。更不用说一个可能对现有版本的 windows 有多种变体。

另一种解决方案是使用带有 UpdateLayeredWindow 的分层窗口。但由于很多原因,这是一种痛苦。

这对我来说是一个死胡同。但是我会给这个问题一个“最喜欢的”标志,如果有更好的东西出现,我想知道它。

于 2010-07-29T16:42:23.263 回答
7

您的问题促使CR 发表了一篇关于 Delphi Haven 的博文……

查看 Stack Overflow,我刚刚注意到一个关于 Aero 玻璃的相当详细的问题(实际上是一组问题)。

于 2010-07-29T10:49:00.840 回答
3

DoubleBuffered 是一种用于减少闪烁的标准图形技术。基本上,您在第二个画布上绘制表单的新版本,然后将其换成当前的。即使绘图过程很慢,这种交换也很快。这与 Aero 之间没有直接关系 - 您可以单独使用其中一个或两个。在 Delphi 中,双缓冲已经存在了很长时间,但现在我们每次屏幕刷新都有更多的处理器周期,这不太必要了。这可能是你没有听说过的原因。

双缓冲是你应该只被动使用的东西——如果你在你的应用程序重新绘制屏幕时看到闪烁,打开它看看会发生什么。在这种情况下,尽管您的第一个办法是 DisableUpdates/EnableUpdates(请参阅 Delphi 帮助)和 Windows API LockWindowUpdate(同上)。

ParentDoubleBuffered 与大多数 Parent... 属性一样,会告诉您此表单是否将使用其父级的 DoubleBuffered 属性。这使您可以在应用程序主窗体上设置一次属性,并使其影响您创建的每个窗体。或者不是,如果您将该属性设置为 false。

当您拥有向后兼容的代码时,这是一种典型的情况——其中有些东西今天很少使用但仍然有效(有时仍然是必要的),尽管大多数人从不需要担心它们。对于大多数人来说,它们就在那里,你可以忽略它们,但对于我们中的一些人来说,它们是非常必要的(我们有几个非常非常复杂的表格,我们偶尔会重新绘制复杂的图案以阻止闪烁)

于 2010-07-29T06:08:32.620 回答
1

好的,我会试着把事情理顺一点。

首先,在屏幕渲染方面,双缓冲是一种非常标准的技术。我一直都在使用它,例如在我的文本编辑器组件中。想象一下需要重绘文本。非常简单地说,我首先清除整个客户端矩形,或多或少地通过FillRect(ClientRect),然后我画线,从第一个可见到最后一个可见,每个从第一个字符到最后一个可见。但这对于最终用户来说会非常难看,在几乎相同的文本内容的两种状态之间有几毫秒左右的时间来清除显示。

解决方案是对屏幕外位图进行所有绘制,并在完成后将屏幕外位图简单地绘制到屏幕上。然后,如果前一帧和新帧相同,则显示不会发生任何事情,这与非双缓冲情况形成鲜明对比,在非双缓冲情况下,屏幕将显示一个空白的空白矩形几毫秒,即屏幕会闪烁。我总是对所有视觉控件使用双缓冲,这确实极大地提高了它们的质量和感觉。在具有千兆字节内存 (RAM) 的现代系统上,内存使用量的增加绝不是问题。而且几乎在所有情况下,缓冲区的交换都足够快(尽管要复制的像素很多)。

在调整窗口大小时,您通常可以观察到缺少双缓冲。有时它们只是闪烁为 h**l。有趣的是,OpenGL 等技术本质上是双缓冲的(至少在大多数情况下是这样)。

所以双缓冲与玻璃片没有任何关系。事实上,DelphiTWinControl的大多数后代都具有DoubleBufferedandParentDoubleBuffered属性(参考)。

ParentDoubleBuffered属性的关联方式与关联 to 、to 、to等的关联DoubleBuffered方式相同。它只是决定控件是否应从其父窗口继承参数值。ParentColorColorParentShowHintShowHintParentFontFont

现在到关于玻璃的问题。好吧,众所周知,向一块玻璃(或玻璃框架)添加控件通常很尴尬,至少在 VCL 应用程序中是这样。有人应该写一篇长篇博客文章讨论如何正确地做到这一点......

于 2010-07-29T12:14:43.860 回答