Search code examples
formsdelphishadowroundingregion

Rounded Form with System Shadow


I tried to do with SetWindowRgn, and I couldn't.

Can do that (the top 2 corners are rounded, the window has a shadow) like on this picture?

enter image description here


Solution

  • Here is a code sample of how to set the window region with shadow:
    (Notes: The Form BorderStyle assumed to be bsNone, not re-sizable)

    type
    TForm1 = class(TForm)
      procedure FormCreate(Sender: TObject);
    private
      procedure CreateFlatRoundRgn;
    protected
      procedure CreateParams(var Params: TCreateParams); override;
    public
    end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.DFM}
    
    procedure ExcludeRectRgn(var Rgn: HRGN; LeftRect, TopRect, RightRect, BottomRect: Integer);
    var
      RgnEx: HRGN;
    begin
      RgnEx := CreateRectRgn(LeftRect, TopRect, RightRect, BottomRect);
      CombineRgn(Rgn, Rgn, RgnEx, RGN_OR);
      DeleteObject(RgnEx);
    end;
    
    procedure TForm1.CreateFlatRoundRgn;
    const
      CORNER_SIZE = 6;
    var
      Rgn: HRGN;
    begin
      with BoundsRect do
      begin
        Rgn := CreateRoundRectRgn(0, 0, Right - Left + 1, Bottom - Top + 1, CORNER_SIZE, CORNER_SIZE);
        // exclude left-bottom corner
        ExcludeRectRgn(Rgn, 0, Bottom - Top - CORNER_SIZE div 2, CORNER_SIZE div 2, Bottom - Top + 1);
        // exclude right-bottom corner
        ExcludeRectRgn(Rgn, Right - Left - CORNER_SIZE div 2, Bottom - Top - CORNER_SIZE div 2, Right - Left , Bottom - Top);
      end;
      // the operating system owns the region, delete the Rgn only SetWindowRgn fails
      if SetWindowRgn(Handle, Rgn, True) = 0 then
        DeleteObject(Rgn);
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      BorderStyle := bsNone;
      CreateFlatRoundRgn;
    end;
    
    procedure TForm1.CreateParams(var Params: TCreateParams);
    const
      CS_DROPSHADOW = $00020000;
    begin
      inherited CreateParams(Params);
      with Params do
      begin
        Style := WS_POPUP;
        WindowClass.Style := WindowClass.Style or CS_DROPSHADOW;
      end;
    end;
    

    Another way to draw a custom shadow would be to set Window WS_EX_LAYERED and use UpdateLayeredWindow

    Here is a very good example of how it's done (sources are in C++ but very easy to understand)

    For more complicated shapes you can use a PNG image on the form and Alpha Blend it.


    EDIT:

    Resizing a WS_POPUP Window is a world of pain... You have a few options:

    NOTE that you need to re-create the Window region when you re-size it (e.g OnResize event).