Jump to content
PeaShooter_OMO

Shadow underneath Form does not always appear (CS_DROPSHADOW)

Recommended Posts

In Delphi 11, Windows 10...

 

I have a small project where I create a form (the same Form object) with two different states; a Normal form and a Borderless Shadowed form (created via CreateParams and CS_DROPSHADOW).

 

After starting the program, if I create the Borderless form first then the shadow will be there. If I create the Normal form first and then the Borderless form then the shadow of the Borderless form will not be there.

 

Attached you will find a .zip file with the project.

Project.zip


Steps to produce the strange behaviour...

  1. Start the program
  2. Create the Normal form first
  3. Close the Normal form via the "Close" button.
  4. Create the Borderless Shadowed Popup form. Notice the shadow does not appear.
  5. You can close and create all you like. The shadow never appears beneath the Borderless form.

 

Steps to produce a shadow at all times...

  1. Start the program
  2. Create the Borderless Shadowed Popup form first. Notice the shadow is correctly appearing.
  3. Close the Borderless form.
  4. Create the Normal form.
  5. Close the Normal form.
  6. Create the Borderless Shadowed Popup form again. Notice the shadow is correctly appearing.
  7. You can flip-clop between the two as much as you like, the shadow will always be there.

 

Obviously I would expect the Borderless form to always have its shadow. Am I doing something wrong?

 

image.thumb.png.5b1f1bdb828b71ae53cbb29cdbd204a9.png


image.thumb.png.4e42ee7c8563fe0ce72e671486dd2aaa.png

type
  TFormMain = class(TForm)
    ButtonCreateNormal: TButton;
    ButtonCreateBorderless: TButton;
    procedure ButtonCreateNormalClick(Sender: TObject);
    procedure ButtonCreateBorderlessClick(Sender: TObject);
  private
  public
  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}

uses UnitPopup;

procedure TFormMain.ButtonCreateBorderlessClick(Sender: TObject);
begin
  FormPopup := TFormPopup.Create(True);
  FormPopup.Show;
end;

procedure TFormMain.ButtonCreateNormalClick(Sender: TObject);
begin
  FormPopup := TFormPopup.Create(False);
  FormPopup.Show;
end;
type
  TFormPopup = class(TForm)
    Panel1: TPanel;
    ButtonClose: TButton;
    procedure ButtonCloseClick(Sender: TObject);
  private
    FIsBorderlessPopup : Boolean;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  public
    constructor Create(AIsBorderlessPopup : Boolean); reintroduce;
  end;

var
  FormPopup: TFormPopup;

implementation

{$R *.dfm}

procedure TFormPopup.ButtonCloseClick(Sender: TObject);
begin
  FreeAndNil(FormPopup);
end;

constructor TFormPopup.Create(AIsBorderlessPopup : Boolean);
begin
  FIsBorderlessPopup := AIsBorderlessPopup;

  inherited Create(nil);

  If FIsBorderlessPopup then
    begin
      Panel1.BorderStyle := bsSingle;
      Panel1.Caption := 'Borderless Shadowed';
    end
  else
    begin
      Panel1.BorderStyle := bsNone;
      Panel1.Caption := 'Normal Form';
    end;
end;

procedure TFormPopup.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);

  If FIsBorderlessPopup then
    begin
      Params.Style := WS_POPUP;
      Params.WindowClass.style := Params.WindowClass.style or CS_DROPSHADOW;
      Params.ExStyle := WS_EX_TOPMOST;
    end;
end;

 

Edited by PeaShooter_OMO

Share this post


Link to post
28 minutes ago, PeaShooter_OMO said:

Am I doing something wrong?

Nothing wrong, just missed "RecreateWnd;" after setting the Params.

Share this post


Link to post
8 minutes ago, Kas Ob. said:

Nothing wrong, just missed "RecreateWnd;" after setting the Params.

I don't get it. Isn't CreateParams called before the window handle is created? Then why would RecreateWnd be necessary?

Share this post


Link to post
24 minutes ago, Anders Melander said:

I don't get it. Isn't CreateParams called before the window handle is created? Then why would RecreateWnd be necessary?

It is beyond me why it is needed, short coming in CM_RECREATEWND that used to trigger the recreation and when it is received, (i think )

 

It could be made better but will break things, also may be things changed in newer VCLs but in the older ones RecreateWnd is needed, even it means the recreation will happen twice.

  • Like 1

Share this post


Link to post

Testing the real project and now i see even with RecreateWnd it is not reliable, and acting as there is something is not initialized.

 

On XE8, the same code with RecreateWnd, act differently with or without debug dcu included in project settings !,

and if there is a break point that halted the code execution then the shadow might appear more frequently. 

Share this post


Link to post

Found the culprit of this discrepancy, but don't have a solution, or lets say nice solution, on top of that my old VCL is irrelevant to the most , so , someone else should have a deeper look into this.

 

There is two different RegisterClass functions, one belong to Delphi RTL and the other is an OS API, Delphi Forms like other controls do register them selves with RegisterClass with unique name, on both API and RTL, the one is causing this problem is API how Windows store the style, Delphi RTL doesn't handle UnregisterClass (API) correctly or not calling it at all, hence CreateParams and the following creating the control (and setting its modern and advanced style) stay short from performing as intended.

 

As a workaround a suggest to refactor your popup into base and two inherited ones hence forcing the class name used by RegisterClass to be different, one with shadow and the other without, this will be the most clean way, though it must be tested.

 

ps: @PeaShooter_OMO don't call "FreeAndNil(FormPopup);" on Self, this is problematic and dangerous, just use "Release;" and it will be released in orderly form, and you can skip the var usage altogether by using 

  with TFormPopup.Create(True) do
    Show;
// or 
  with TFormPopup.Create(False) do
    Show;

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×