Delphi support for Aero Glass and the DoubleBuffered property - what is going on and how do we use them?

狂风中的少年 提交于 2019-11-28 16:33:43

About DoubleBuffer

.NET may have one, and it may have the same name and the same purpose as Delphi's one, but Delphi is implementing DoubleBuffer from ground-up and I assume .NET does the same. No window style bits are used implementing this.

DoubleBuffer and Glass Aero

Fairly simple: Don't set DoubleBuffer for controls that sit on Glass. For DoubleBuffering to work one has to be able to initialize the "Buffer" - but what to initialize it with for Glass? DoubleBuffering is not required for Windows standard controls (including TButton). For new controls that need both transparent surfaces and doublebuffer-like behaviour, one can use the Layered windows api's.

Getting controls to work on Glass

Step 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;

Step 2, this should be your form's OnPaint handler:

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;

Step 3: Set GlassFrame.Enabled = True; Set all other Glass properties, add controls to the form, wherever you like them. May be on Glass or anywhere else. Make sure the controls don't have "DoubleBuffered = True". That's it, enjoy. I've tested with TButton, TCkBox and TEdit.

... EDIT ...

Unfortunately using this method "Glass" is treated as an 100% transparent surface, and it's not - it looks like glass, but it doesn't behave like glass. The problem with 100% transparency is, if you click on that transparent area, your click goes to the window behind your window. Horrible.

At the time of this writing I'm pretty sure there's no API to change the default BLACK key color for the original glass (google finds countless blog and forum posts on how you need to use custom drawing for controls that sit on glass and there's no function to change that in the list of DWM functions on MSDN). Without changing the default BLACK color most controls can't render properly because they write text using clWindowText and that's BLACK. One suggested trick found on several forums is to change the transparency color using the SetLayeredWindowAttributes API. And it works! Once that's done black text on controls shows throw, but unfortunately glass is no longer glass, glass looks like glass but behaves like 100% transparency. This pretty much invalidates this solution and shows an double standard on Microsoft's side: The original BLACK does not behave like 100% transparency yet if we change it to something better it does behave like 100% transparency.

In my opinion the common thinking of using custom-controls on Glass is wrong. It's the only thing that might work, but it's wrong, because we're supposed to use controls that are consistent across the platform: suggesting custom controls opens the door to inconsistent, winamp-like applications, where each user re-creates the wheel to suit it's artistic ideas. Even if an developer manages to faithfully recreate any given windows control and make it work on glass, the "fix" is only temporary and needs to be re-created for the next version of windows. Not to mention one should probably have multiple variants for the existing versions of windows.

The other solution is to use Layered windows with UpdateLayeredWindow. But that's a PAIN for sooo many reasons.

This is an dead end for me. But I'll give the question an "favorite" flag, if something better shows up I'd like to know about it.

Your question has prompted a blog post from CR on Delphi Haven...

Looking on Stack Overflow, I’ve just noticed a rather detailed question (really, set of questions) about Aero glass.

DoubleBuffered is a standard graphics technique used to reduce flicker. Basically, you draw a new version of your form on a second canvas then swap it for the current one. That swap is fast, even if the drawing process is slow. There's no direct relationship between that and Aero - you can use either or both independently. Double buffering has been around very a very long time in Delphi, but now we have a lot more processor cycles per screen refresh it's less necessary. Which may be why you haven't heard of it.

Double buffering is something you should only use reactively - if you see flicker when your app is repainting the screen turn it on and see what happens. In that case though your first recourse would be DisableUpdates/EnableUpdates (see the Delphi help for those) and the Windows API LockWindowUpdate (ditto).

ParentDoubleBuffered, like most Parent... properties, tells you whether this form will use the DoubleBuffered property from its parent. This lets you set the property once on your app main form and have it affect every form you create. Or not, if you set that property to false.

This is a typical situation when you have backward compatible code - there's things in there that are rarely used today but still work (and are sometimes still necessary), even though most people never need to worry about them. For most people they're just there and you can ignore them, but for some of us they're desperately necessary (we have a couple of really, really complex forms that we redraw in occasionally complex patterns to stop the flickering)

OK, I will try to straighten things out a bit.

First of all, double-buffering is a very standard technique when it comes to on-screen rendering. I use it all the time, for instance in my text editor component. Imagine the text needs to be redrawn. Very simply put, I first clear the entire client rect, more or less by FillRect(ClientRect), then I draw the lines, from the first to the last visible, each from the first character to the last visible. But this will look very ugly to the end user, having a few milliseconds or so with a cleared display, between two states of almost the same textual content.

The solution, is to do all drawing to an off-screen bitmap, and simply draw the off-screen bitmap onto the screen when it is complete. Then, if the preveous and the new frame are identical, there will happen nothing with the display, in great contrast to the non-double-buffered case, where the screen will display an empty, white rectangle for a few milliseconds, i.e. the screen will flicker. I always use double-buffering for all my visual controls, and this really improves the quality and feel of them, vastly. On a modern system with gigabytes of memory (RAM), the increased memory usage is by no means a problem. And in almost all cases, the swapping of the buffers is more than fast enough (although there are quite a few pixels to copy).

Often you can observe the lack of double-buffering when resizing windows. Sometimes they just flicker as h**l. It might also be interesting to notice that technologies such as OpenGL are inherently double-buffered (in most cases, at least).

So double-buffering has nothing in particlar to do with sheets of glass. In fact most decendants of the Delphi TWinControl has the DoubleBuffered and ParentDoubleBuffered properties (reference).

The ParentDoubleBuffered property is ralated to DoubleBuffered in the same way as ParentColor is related to Color, ParentShowHint to ShowHint, ParentFont to Font, etc. It simply decides whether or not the control should inherit the value of the parameter from its parent window.

Now to the question about the glass. Well, it is very common knowledge that it in general is awkward to add controls to a sheet of glass (or a glass frame), at least in a VCL application. Someone should write a long blog article discussing how to do that properly...

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!