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.