Non-rectangular translucent forms

By: Philippe Randour

Abstract: This article shows you how, on Windows 2000/XP, you can easily add a non-standard user interface to your application by taking advantage of translucency and transparency.

Non-rectangular translucent forms

Non-rectangular translucent forms

by Philippe Randour (philippe_randour@hotmail.com)

Today, non-standard user interfaces are commonplace. You can see it in action in tools like WinAmp or the Windows Media Player. But now, you can do it too very easily and add some impact to your application.

The technique I will present in this article is only available to software running on Windows 2000/XP, because it relies on an API introduced with Windows 2000. So, your application should check for those versions at startup and exit gracefully if a match is not found.

The way to create non-rectangular forms, that is forms with irregular shapes and transparent parts, with some level of translucency is through the use of a new functionality called layered windows.

Checking for Windows 2000/XP

In the OnCreate event of the main form of your application, add the following lines:

if (Win32Platform <> VER_PLATFORM_WIN32_NT) or 
   (Win32MajorVersion <> 5) then
  Application.Terminate;


In this code, I do not use any Win32 API. Instead, I use global variables provided at run-time by the VCL. Those are defined in the SysUtils unit, and their value is computed at the start of the application. If you need more control over the Windows version, the following variables are available:

Win32Platform identifies the operating system platform
Win32MajorVersion identifies the major version number of the operating system
Win32MinorVersion identifies the minor version number of the operating system
Win32BuildNumber identifies the build number of the operating system
Win32CSDVersion identifies the latest Service Pack installed on the system

Creating a layered window

To create a layered window, you have to add an extended window style to your form: WS_EX_LAYERED. It can be done by overriding the CreateParams method of the form:

interface

type
  TSkinForm = class(TForm)

  ...

protected
  procedure CreateParams(var Params: TCreateParams); override;

  ...

end;

implementation

const
  WS_EX_LAYERED = $80000;

procedure TSkinForm.CreateParams(var Params: TCreateParams);
begin
  inherited;
  with Params do
    ExStyle := ExStyle or WS_EX_LAYERED;
end;

Depending on the Win32 API translation that is installed on your computer, the WS_EX_LAYERED constant might already be defined.

Adding transparency and translucency

The Win32 API that makes transparency and translucency a reality is SetLayeredWindowAttributes:

type
  TSetLayeredWindowAttributes = function (hWnd: HWND; crKey: TColorRef; 
    bAlpha: Byte; dwFlags: LongWord): LongBool; stdcall;

Its parameters are defined as:

hWnd the handle to the layered window. It is given by the Handle property of the TForm object.
crKey the transparency color key used when composing the layered window. All pixels of the form using this color will be transparent.
bAlpha a value used to define the translucency of the layered window. When this parameter is 0, the window is transparent. When this parameter is 255, the window is opaque.
dwFlags indicates the action to perform.
Possible values are:
LWA_COLORKEY: use crKey as the transparency color.
LWA_ALPHA: use bAlpha to define the translucency of the layered window.

So, if you want to make your form 50% translucent and with all pixels of color clBlue transparent, you will add some code to the OnCreate event:
interface

type
  TSkinForm = class(TForm)
 
  ...

  procedure FormCreate(Sender: TObject);

  ...

end;

implementation

const
  LWA_COLORKEY = 1;
  LWA_ALPHA = 2;

procedure TSkinForm.FormCreate(Sender: TObject);
var
  TransparencyColor: TColorRef;
  Translucency: Byte;
  SetLayeredWindowAttributes: TSetLayeredWindowAttributes;
  User32: HMODULE;
begin
  TransparencyColor := clBlue;
  Translucency := (255 * 50) div 100;

  User32 := SafeLoadLibrary('user32.dll');
  if User32 <> 0 then
  begin
    SetLayeredWindowAttributes := GetProcAddress(User32,
      'SetLayeredWindowAttributes');
    if @SetLayeredWindowAttributes <> nil then
      SetLayeredWindowAttributes(Handle,
        TransparencyColor,Translucency,LWA_ALPHA or LWA_COLORKEY);
    FreeLibrary(User32);
  end;
end;

Adding transparency to a form also means that all areas that are color-keyed will not capture mouse clicks. Those clicks will be directed to whatever is on the screen at this place.

Moving a form with no title bar

When you create an application with a non-standard UI, you want to get rid of the classic window border, as well as the title bar. So, you set the BorderStyle for the form to bsNone. But you still want to be able to move your form like for a standard application. You can do it by handling the WM_NCHITTEST message sent by Windows, just slightly changing its behaviour. This way, when the user clicks anywhere in the client area of the form, Windows will think that it was done on the title bar and react accordingly, allowing the form to be moved.


interface

type
  TSkinForm = class(TForm)

  ...

protected
  procedure WMNCHitTest(var Msg: TMessage); message WM_NCHITTEST;

  ...

end;

implementation

procedure TSkinForm.WMNCHitTest(var Msg: TMessage);
begin
  inherited;
  if Msg.Result = HTCLIENT then
    Msg.Result := HTCAPTION;
end;

Conclusion
In this article, I have introduced the API available in Windows 2000/XP to add easily a cool look to your application and make it more appealing, by current standards, to the end-user.


Server Response from: ETNASC02