Creating transparent windows

By: Corbin Dunn

Abstract: How to make transparent portions on your window with Delphi.

Creating Transparent Windows
And moving around the children on that window
by Corbin Dunn

Download the sample source code and project for this article.

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.

Server Response from: ETNASC03