“Have you ever seen the rain (on your background image through a Delphi window)?” Ok, sorry to all the Creedence Clearwater Revival fans out there ;-) . Today we wanna talk about how to achieve translucent Delphi windows with Windows Vista. I know this does not sound like a big deal for all the lucky owners of Delphi 2007 since this feature is already implemented in this release.

Now let’s have a look at the Desktop Window Manager (DWM) API. The DWM is responsible for the composition of windows on the desktop and the DWM API allows developers to control how composition affects a particular window. As you will see, the DWM is responsible for much more than just “glass”.

When it comes to graphics, the terms can be confusing. Before working with transparency and translucency in Windows we should clarify some of them.

Terminology

Transparency – Refers to the ability to see through something clearly and without obstruction. Think of it as clear glass. Some applications and APIs use the term transparency to refer to a scale that ranges from “completely” transparent to “completely” opaque.

Translucency – People often use translucency and transparency interchangeably but they actually mean very different things. Translucency refers to the ability to see through something where the background appears unclear whether it is out-of-focus or simply blurry in some way. Windows Vista refers to the glass effect as “transparent glass” when it technically should be called translucent glass.

Opacity – Opacity refers to the state of being opaque and opaque refers to something that is neither transparent nor translucent. Some applications and APIs use the term opacity to refer to a scale that ranges from completely opaque to completely transparent.

Alpha Channel – An alpha channel provides additional information for each pixel in an image that facilitates compositing images together.

Window Regions – A window’s region determines the area within the window where system permits painting. Although Windows 95 supported window regions, it was not until Windows XP that the default theme used regions to present windows with rounded corners. Although the default Windows Vista theme also presents windows with rounded corners, regions are no longer used unless you resort to the Windows Vista Basic theme.

Glass – Glass is the catchy marketing terms that Windows Vista uses to refer to translucency.

Blur – Some of the DWM APIs refer to blur and again this indicates translucency. Presumably, the Windows developers felt it was easier to spell and comprehend.

Desktop Composition – The DWM performs desktop composition, enabling visual effects on the desktop such as glass, 3D window transitions, etc.

RGB – RGB is short for Red, Green and Blue. RGB values are typically packed into a COLORREF (which is just a Cardinal) as follows: $00BBGGRR. As you can see, the first byte is always zero and the remaining three bytes store the individual red, green and blue values in reverse order. Each color value ranges from zero through 255. If all three values are zero then the result is black. If all three values are 255 then the result is white. For example, to represent red specify $000000FF. As you can see, RGB does not provide an alpha channel.

ARGB – ARGB is short for Alpha, Red, Green and Blue. ARGB values are typically packed into an ARGB (which is just a Cardinal) as follows: $AARRGGBB. The first byte stores the alpha value and the remaining three bytes store the red, green and blue values. Note that the color values are stored in the opposite order to RGB.

GDI – The Windows Graphics Device Interface (GDI) API is the original graphics interface used for 2D drawing in Windows. With the exception of a few newer functions, the GDI API does not honor the alpha channel in images. GDI uses RGB values to represent color. The GDI API is wrapped in our beloved Graphics unit.

GDI+ – GDI+ was introduced with Windows XP (and Windows Server 2003) to provide a more capable programming model for 2D drawing, imaging and typography and fully supports alpha blending. GDI+ uses ARGB values to represent color. Although it is not necessary to have GDI+ support in Delphi it is a nice feature and you will need it for drawing black lines and text on “glass” windows. Why is that? You will see. You can obtain the units for GDI+ at http://www.progdigy.com/modules.php?name=gdiplus

Now for some code

If you do not want to read the following stuff you may want to look at the example project at: http://theunknownones.googlecode.com/svn/Libraries/DWMHelper/

The first thing you’ll need is to put the following units in your uses clause

JwaDwmApi,
JwaUxTheme

The most important one of them is the first one since it wraps up all the DWM API has to offer. Let us now look how to achieve things. Don’t be afraid of looking into JwaDwmApi.pas. It looks much more complicated than it is. As always Microsoft developers seem to love passing records to their functions. I don’t love that all to much so I wrote some helper functions to make live easier. Those can also be found in TUO’s SVN http://theunknownones.googlecode.com/svn/Libraries/DWMHelper/

The simplest thing to do is to blur the background behind a window:

function DWM_EnableBlurBehind(hwnd : HWND; AEnable: Boolean; hRgnBlur : HRGN = 0; ATransitionOnMaximized: Boolean = False; AFlags: Cardinal = 1): HRESULT;
var
  bb : DWM_BLURBEHIND;
begin
  bb.dwFlags:=AFlags;
  bb.fEnable:=AEnable;
  bb.hRgnBlur:=hRgnBlur;
  bb.fTransitionOnMaximized:=ATransitionOnMaximized;

  Result:=DwmEnableBlurBehindWindow(hwnd, bb);
end;

You can call the function this way:

DWM_EnableBlurBehind(Form1.Handle, true);

Now your window looks like this, easy isn’t it`?

EnableBlurBehind

HELP!!! My Window is opaque and/or white!!! What did I do wrong?
There can be different reasons for that

  1. You do not have Windows Vista installed. (Ok this sounds stupid… but I really got that issue with some newbies)
  2. You only have Vista Home Personal installed or the Destkop Window Manager Service is switched off. (Translucent Windows are only supported under Aero)
  3. Your Form1.Color is different from clBlack=$00000000 (WHY THAT? Calm down ;-) I’ll explain)

Why it works with black windows only

As we have learned in the terminology section Delphi and Windows Vista use different color values (RGB vs. ARGB, remember?) Now Delphi draws only RGB colors with the help of GDI routines. Windows however expects ARGB values and assumes to receive one. So the color Windows assumes to be the right one is in most cases the wrong one, due to different byte order and missing alpha value.

How can we pass a valid value to Windows? As we want to receive a translucent window the color we should set to the window is transparent. The ARGB value for transparent is $00000000. Which color could we take in RGB to represent transparent? Right: BLACK. This is a major drawback for us. Most of the labels, buttons and edits etc. use black as their default foreground color. Now They all will become transparent. You may have or will read various attempts using SetLayeredWindowAttributes in order to change the transparent color but it seems this worked with some preview releases of Vista only.

How can you draw something black, transparent etc. on the window? Use GDI+…

More Glass

The vigilant reader may have observed that DWM_EnableBlurBehind takes a region as parameter. With this parameter you can pass a more or less complicated region to the DWM to tell it where do draw translucent. Try the following:

var
  rgn : HRGN;
begin
  rgn:=CreateEllipticRgn(0,0,Form1.clientwidth,Form1.clientheight);
  DWM_EnableBlurBehind(Form1.Handle, true, rgn, false, DWM_BB_ENABLE or DWM_BB_BLURREGION);
end;

And voilà

EnableBlurBehind Elliptic Region

Making the Frame thicker

In order to control the drawing of the blurred window frame you gotta consider another function called DwmExtendFrameIntoClientArea. Because of my lazyness I introduces a little helper:

function DWM_ExtendFrameIntoClientArea(hwnd: HWND; ATopHeight, ALeftWidth, ABottomHeight, ARightWidth: Integer): HRESULT;
var
  lMargins : Margins;
begin
   lMargins.cyTopHeight := ATopHeight;
   lMargins.cyBottomHeight := ABottomHeight;
   lMargins.cxLeftWidth := ALeftWidth;
   lMargins.cxRightWidth := ARightWidth;

   Result := DwmExtendFrameIntoClientArea(hwnd, lMargins);
end;

Calling it this way

DWM_ExtendFrameIntoClientArea(Form1.Handle, 50, 50, 50, 50);

Leads to that result:

DwmExtendFrameIntoClientArea

I don’t want a Frame at all

Sometimes this effect is referred to as “Sheet Of Glass” I don’t know how Microsoft developers call it since they ‘abuse’ one of their own functions to reach that effect. I personally would have introduced a distinct function for that. But it shows that people at Microsoft put on their pants one leg at a time as well.

DWM_ExtendFrameIntoClientArea(Form1.Handle, -1, -1, -1, -1);

Now you sould see something like that:

Sheet of Glass

Conclusion

There isn’t really more magic behind it than that. Of course this is a very raw approach. For example the effect will collapse as soon as the screensaver switched on and back off. One could call the DWM_* routines in CreateWindowHandle for example.