Transparent and colorful?

Posted: (EET/GMT+2)

 

Transparent and colorful?

May 12, 2000

Delphi 5 Professional & Enterprise

If you are using Windows 2000, you might have noticed that by default all menus use a fancy fade-out feature that makes the selected menu command fade away after you select the command. Well, you can use similar effects in your own Delphi 5 applications is you just take a minute to study a couple of new APIs.

Although Windows 2000 looks pretty similar to Windows NT 4.0 and Windows 95/98, the brand new Windows operating system has many new advanced features and APIs that are open to the software developer. Several new API calls are related to displaying completely transparent or even partly transparent windows. In its own documentation, Microsoft calls these special windows layered windows.

the example program "amazoapp" uses layered windows to create "holes".

Layered windows are special in the way they are painted. Normally, all windows paint themselves by responding to the Windows WM_PAINT message, or to the OnPaint event handler, which is more familiar to Delphi programmers. When your application uses layered windows, Windows will internally redirect your paint handling, and then automatically apply transparency effect to anything you paint.

Creating the window

To use the features provided by layered windows, you first need to create a window with the CreateWindowEx Windows API call. You also need to include the WS_EX_LAYERED constant along with the other window style flags you specify. When programming directly with the Windows API, knowing this is mandatory, but when you are using Delphi, you are far better off by letting Delphi create your window (form), and then setting the window style afterwards.

You can set the window style simply by using the SetWindowLong API function, which is declared as follows in Windows.pas:

function SetWindowLong(hWnd: HWND; nIndex: Integer;
                       dwNewLong: Longint): Longint; stdcall;

For example, you might want to call the function like this in the OnShow event handler of your form:

procedure TAmazoAppMainForm.FormShow(Sender: TObject);
begin
  SetWindowLong(Handle,GWL_EXSTYLE,GetWindowLong(Handle,GWL_EXSTYLE)
                Or WS_EX_LAYERED);
end;

Having enabled the layered window mode, you are free to call the layered window API calls.

The layered window APIs

When working with layered windows, you need master at least two API functions, SetLayeredWindowAttributes and UpdateLayeredWindow. For most simple applications you will only need the first one, but if you wish to do animation or other such fancy effects, you might want to use UpdateLayeredWindow.

Unfortunately, Delphi 5's own header file don't include the necessary translations from the Windows Platform SDK, so you might want to use the following declarations:

Function SetLayeredWindowAttributes(hWnd : hWnd; crKey : TColorRef;
                                    bAlpha : Byte; dwFlags : Integer) : Integer;
                                    StdCall; External User32
                                    Name 'SetLayeredWindowAttributes';

Function UpdateLayeredWindow(hWnd : hWnd; hdcDst : hDC; pptDst : PPoint;
                             psize : PSize; hdcSrc : hDC; pptSrc : PPoint;
                             crKey : TColorRef; pblend : PBlendFunction; 
                             dwFlags : Integer) : Integer; StdCall; External User32
                             Name 'UpdateLayeredWindow';

Also, you will need the following constants and type declarations when working with layered windows:

Const
  WS_EX_LAYERED = $00080000;
  LWA_COLORKEY  = $00000001;
  LWA_ALPHA     = $00000002;
  ULW_COLORKEY  = $00000001;
  ULW_ALPHA     = $00000002;

Type
  PBlendFunction = ^TBlendFunction;
  TBlendFunction = Packed Record
    BlendOp : Byte;
    BlendFlags : Byte;
    SourceConstantAlpha : Byte;
    AlphaFormat : Byte;
  End; 

The example program

To give you an idea what layered windows can do for your application, we've written a sample application that you can download by using the link below. The application features a main window, which has a transparent, round area in it. You can actually see through this "hole" and even click through it.

Of course, you could also achieve similar effects by creating your windows with the WS_EX_TRANSPARENT style, but the example program uses colors and layered windows for this purpose. Consider the following form on the design time:

amazoapp main form at design time

The layered window APIs let you specify one color, that is going to be transparent. The following code sets the color of the round shape (clGreen) to be totally transparent:

SetLayeredWindowAttributes(Handle,Shape1.Brush.Color,0,LWA_COLORKEY);

Although it is convenient to specify a totally transparent color with only a single API call, you might want translucency, or partial transparency too. The example program uses this feature to create a fading splash screen. All that is needed is a timer and simple integer variable that is increased step by step from zero to 255, which happens to be the maximum value of transparency accepted by the SetLayeredWindowAttributes function.

Because of the automatic message redirection mentioned earlier, the splash screen code must use few tricks to work properly. Because the splash screen fades in from complete transparency, the first painting in the OnPaint event handler must take into account the background that happens to be underneath the splash screen. This is handled as follows:

procedure TSplashScreen.FormPaint(Sender: TObject);
Var
  P     : TPoint;
  Bkgnd : TCanvas;

begin
  P := ClientToScreen(Point(0,0));
  Bkgnd := TCanvas.Create;
  Bkgnd.Handle := GetDC(0); { the screen }
  Canvas.CopyRect(Rect(0,0,ClientWidth,ClientHeight),Bkgnd,
                  Rect(P.X,P.Y,P.X+ClientWidth,P.Y+ClientHeight));
  Bkgnd.Free;
  ImageList1.Draw(Canvas,0,0,0);
end;

The idea is to first copy the screen to a temporary canvas, and the draw it again onto the form. Then, the code uses a TImageList to transparently draw the splash screen figure:

the splash screen figure

It is also worthwhile to note that the normal windows background erasing function is disabled by handling the WM_ERASEBKGND Windows message.

Fading in

The fading of the splash figure is completely handled by the timer event:

procedure TSplashScreen.Timer1Timer(Sender: TObject);
begin
  Inc(Transparency,10);
  If (Transparency >= 255) Then Begin
    Timer1.Enabled := False;
    Timer2.Enabled := True; { to eventually close the form }
    Transparency := 255;
  End;
  SetLayeredWindowAttributes(Handle,0,Transparency,LWA_ALPHA);
end;

Initially, the value of the Transparency variable is set to zero, meaning complete transparency. As the timer hits again, the value is increased, and the splash screen is always redrawn with less transparency. Note that because the screen background was copied before the fade, only the splash image appears to fade in instead of the whole rectangle-shaped splash form.

Finally, the Transparency variable reaches the maximum value and the fading stops. The example program uses a second timer component to keep the splash screen around for a few more seconds, and then displays the main form. Cool, no?

Download the example code

Download transparentandcolorful.zip (241 kB) which contains the sample application "AmazoApp". Please note that the sample application will only run on Windows 2000.

* * *

Want to have cool features in your own applications? Let us help you!