You may have seen some windows that have transparent portions, and wonder how you can do that in Delphi. It is quite easy, and there are several different ways to achieve the affect such as below:
The first way you could go about this is by adding the WS_EX_TRANSPARENT style to your window. This will work, but it will not have all the desired effects, and Microsoft recommends using the WS_EX_TRANSPARENT style for modal windows that have a short life.
The second way of doing this is with Windows 2000's new Layered Windows feature. However, doing this will limit your application to only run on Windows 2000.
The third approach is the one chosen in this article. You can use the Windows API call SetWindowRgn to specify the exact portion of the window that you want seen. One of the tough parts is creating a region that contains all child windows that you want seen. The solution is to go through all visible Controls on the form, and create one gigantic region that combines all these small regions. Here is how to do this:
procedure TForm1.SetRegions;
var
I: Integer;
RgnAll, RgnCtrl: HRGN;
begin
RgnAll := 0;
for I := 0 to ControlCount - 1 do
begin
with Controls[I] do
begin
if Visible then
begin
// Create a region for each given visible control
RgnCtrl := CreateRectRgn(Left, Top, Left + Width, Top + Height);
// Combine the region with all previous ones, if available
if (RgnCtrl <> 0) and (RgnAll <> 0) then
begin
CombineRgn(RgnAll, RgnAll, RgnCtrl, RGN_OR);
DeleteObject(RgnCtrl);
end
else
RgnAll := RgnCtrl; // This is the first region being created
end;
end;
end;
// Now, set the RgnAll as what we see for the Window
if RgnAll <> 0 then
begin
(*
From SetWindowRgn in the help file:
"After a successful call to SetWindowRgn,
the operating system owns the region specified
by the region handle hRgn. The operating system
does not make a copy of the region. Thus, you
should not make any further function calls with
this region handle. In particular, do not close
this region handle."
So don't call DeleteObject on RgnAll after using
it for SetWindowRgn (thanks to Richard Albury for
pointing this out!) A previous version of this article
made this mistake.
*)
SetWindowRgn(Handle, RgnAll, True);
end;
end;
Note how I call DeleteObject after I was finished using the region. Not doing this will cause resource leaks on Windows. I use the API CreateRectRgn, but if you have a different shape you can use CreatePolygonRgn to customize the exact shape of the Window or Control.
One problem that you may encounter deals with moving controls on the form. If you programatically move the control (such as in an OnMouseMove event), the area behind the control will be out of the view of the control. Also, the control may not be properly painted while it is being moved. There is a simple solution for this: call SetRegions again to update the new visible area of the form, and force a repaint of the control with a call to Control.Repaint, such as below:
procedure TForm1.GenericMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
// If the control wasn't moved, then exit right away
if (X - LastX = 0) and (Y - LastY = 0) then Exit;
// Move the control
with (Sender as TControl) do
begin
Left := Left + (X - LastX);
Top := Top + (Y - LastY);
end;
SetRegions;
(Sender as TControl).Repaint;
end;
You should now be able to add transparent windows to your application. Download the sample source code and project.