代码之家  ›  专栏  ›  技术社区  ›  Warren P

Delphi支持Aero Glass和Doublebuffered属性-发生了什么事,我们如何使用它们?

  •  25
  • Warren P  · 技术社区  · 14 年前

    我对Delphi2009/2010对Windows中Aero主题玻璃功能的支持感到困惑,也对Doublebuffered的确切含义以及它与Aero玻璃的关系感到困惑。我发现doublebuffered不仅是VCL中的一个属性,它还存在于 .net WinForms . 最初我想知道它是否设置了公共控件库使用的某种窗口样式位,或者什么。为什么要使用它,什么时候应该使用它?

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

    特别是,我对DoubleBuffered属性感到困惑,我想知道它存在的原因,以及它在一个窗体和一个控件中的玻璃支架和Double Buffered属性之间的关系是什么。当你阅读 C++ articles like this one 您可以看到没有提到双缓冲。

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

    我发现一些C++开发者谈论他们如何调用SETALAEDED WINDOWS属性,以避免当你在经典的Win32应用程序中切换时,DWM/AIR合成引起的“黑色变成玻璃”毛刺[但是下面的博客链接告诉我,这不再在Windows 7中工作,实际上只在Vista短暂工作,直到微软BL。扣紧它。[开始错误的想法]难道我们不应该使用其他颜色,如明亮的洋红,使之变成玻璃透明色吗?[结束错误的想法]

    什么时候应该设置而不设置双缓冲的规则是什么?为什么首先要将双缓冲添加到VCL中?什么时候设置会导致问题?(看起来远程桌面是一种情况,但这是唯一的情况吗?)如果不设置,按钮文本的渲染就会出现问题,很可能是因为Delphi没有更改Aero DWM中默认的“渲染为玻璃般的黑色”。

    在我看来,Aero Glass渲染基本上是以一种奇怪或难以理解的方式进行的(由Windows本身,而不是Delphi,它仅仅包装了这个功能),并且在stdctrls的类中,2009/2010年的许多内部VCL源代码必须执行许多复杂的逻辑才能在Aero Glass上正确地呈现内容,但是它是sti我有很多问题,看起来好像做错了,这可能是背后的原因。 related question, and qc issue. [update3:很多渲染玻璃上的小故障,在VCL中,在公共控件中渲染错误,看起来微软并不关心修复。简言之,Delphi VCL代码修复无法修复这样一个事实:古老的Windows公共控件库和现代(但奇怪的)航空玻璃合成功能彼此不太喜欢,而且不能很好地协同工作。感谢微软建立了如此高质量的技术,并将其推广到世界各地。]

    如果还不够有趣的话,为什么我们要双亲呢?

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

    4 回复  |  直到 14 年前
        1
  •  15
  •   Cosmin Prund    14 年前

    关于DoubleBuffer

    .NET可能有一个,它可能与Delphi的名称和目的相同,但Delphi从一开始就实现了DoubleBuffer,我认为.NET也有同样的功能。没有使用窗口样式位来实现这一点。

    双缓冲和玻璃航空

    相当简单:不要为安装在玻璃上的控件设置双缓冲。要使双缓冲工作,必须能够初始化“缓冲区”——但是要用什么来初始化玻璃缓冲区呢?Windows标准控件(包括tbutton)不需要双重缓冲。对于同时需要透明表面和双缓冲行为的新控件,可以使用分层WindowsAPI。

    让控制装置在玻璃上工作

    步骤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 :set glassframe.enabled=true;设置所有其他玻璃属性,将控件添加到表单中您喜欢的任何位置。可能在玻璃上或其他任何地方。确保控件没有“DoubleBuffered=true”。就这样,享受吧。我用tbutton、tckbox和tedit测试过。

    …编辑…

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

    在写这篇文章的时候,我很确定没有API可以改变原始玻璃的默认黑键颜色(谷歌发现了无数的博客和论坛文章,介绍了你需要如何使用自定义绘图来控制放置在玻璃上的控件,并且没有任何功能可以在 the list of DWM functions on MSDN )如果不更改默认的黑色,大多数控件就无法正确呈现,因为它们使用clwindowtext编写文本,而这是黑色的。在几个论坛上发现的一个建议技巧是使用setlayeredWindowAttributes API更改透明度颜色。它工作!完成后,控件上的黑色文本显示“抛出”,但不幸的是,玻璃不再是玻璃,玻璃看起来像玻璃,但行为像100%的透明度。这几乎使这个解决方案失效,并在微软方面显示了一个双重标准:原始的黑色并不像100%透明那样工作,但是如果我们把它改成更好的,它的行为就像100%透明一样。

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

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

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

        2
  •  7
  •   Roddy    14 年前

    你的问题引起了 a blog post from CR on Delphi Haven

    查看堆栈溢出,我只是 注意到一个相当详细的问题 (真的,一组问题)关于航空 玻璃。

        3
  •  3
  •   Мסž    14 年前

    doublebuffered是一个 standard graphics technique 用于减少闪烁。基本上,在第二个画布上绘制表单的新版本,然后将其替换为当前版本。即使绘图过程很慢,交换也很快。这和航空没有直接关系——你可以单独使用,也可以同时使用。在Delphi中,双缓冲已经存在很长时间了,但是现在我们有了更多的处理器周期,每次屏幕刷新都不需要这样做。这可能是你没听说过的原因。

    双缓冲是你应该只使用反应-如果你看到闪烁当你的应用程序重新绘制屏幕,打开它,看看会发生什么。在这种情况下,您的第一个方法是禁用更新/启用更新(请参阅Delphi帮助)和Windows API LockWindowUpdate(同上)。

    双亲,就像大多数父母一样…属性,告诉您此窗体是否将使用其父级的DoubleBuffered属性。这允许您在应用程序主窗体上设置一次属性,并使其影响您创建的每个窗体。如果您将该属性设置为false,则为否。

    这是一种典型的情况,当您有向后兼容的代码时——其中有一些东西现在很少使用,但仍然可以工作(有时仍然是必要的),即使大多数人不需要担心它们。对大多数人来说,它们只是在那里,你可以忽略它们,但对我们中的一些人来说,它们是极其必要的(我们有一些非常非常非常非常复杂的形式,我们偶尔会以复杂的模式重新绘制以阻止闪烁)。

        4
  •  1
  •   Andreas Rejbrand    14 年前

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

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

    解决方案是,对 离屏 位图,完成后,只需将屏幕外位图绘制到屏幕上即可。然后,如果前一帧和新帧是相同的,显示屏将不会发生任何变化,这与非双缓冲的情况形成了很大的对比,在这种情况下,屏幕将显示一个空的白色矩形,持续几毫秒,即屏幕将闪烁。我总是使用双缓冲 全部的 我的视觉控制,这真的大大提高了它们的质量和感觉。在一个拥有千兆字节内存(RAM)的现代系统中,增加的内存使用绝对不是问题。在几乎所有情况下,缓冲区的交换速度都足够快(尽管有相当多的像素需要复制)。

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

    所以双缓冲与玻璃板没有任何特殊关系。事实上,大部分德尔菲人 TWinControl DoubleBuffered ParentDoubleBuffered 性能( reference )

    这个 父级双缓冲 属性已关联到 双缓冲的 以同样的方式 ParentColor 与…有关 Color , ParentShowHint ShowHint , ParentFont Font 等等,它只是决定控件是否应该从其父窗口继承参数的值。

    现在谈谈玻璃的问题。一般来说,在玻璃板(或玻璃框)上添加控件是很难的,至少在VCL应用程序中是这样。应该有人写 a long blog article 讨论如何正确地做到这一点…

    推荐文章