Creating a Yosemite shadow for borderless form in Delphi [part 3]
In this part we are going to use the shadow form from out project source code and main form.
program ShadowDemo;
{$R *.dres}
uses
Vcl.Forms,
Yosemite in 'Yosemite.pas' {Form1},
FormShadow in 'FormShadow.pas' {frmShadow};{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TfrmShadow, frmShadow);
Application.Run;
end.
We must make sure that our FormShadow.pas is included in the Uses section of our project code and also in the main form. As well we need it to be created after our main form as shown in the code above.
procedure TForm1.FormCreate(Sender: TObject);
begin
BorderStyle := bsNone;
Position := poScreenCenter;
Application.OnActivate := OnFocus;
Application.OnDeactivate := OnUnfocus;
Constraints.MinHeight := 160;
Constraints.MinWidth := 160;
DoubleBuffered := True;
end;
Let’s start with the FormCreate procedure, where we set our main form with no border style, assign OnActivate and OnDeactivate procedures that are going to be crucial for our main form to be assigned with the correct shadow if it is focused or we are not using this window.
procedure TForm1.OnFocus(Sender: TObject);
begin
frmShadow.ActivateShadow := True;
end;procedure TForm1.OnUnfocus(Sender: TObject);
begin
frmShadow.ActivateShadow := False;
end;
Setting ActivateShadow to true or false, will immediately update the shadow type.
procedure TForm1.FormShow(Sender: TObject);
begin
frmShadow.ClientWidth:=ClientWidth+frmShadow.Margins.Left+frmShadow.Margins.Right;
frmShadow.ClientHeight:=ClientHeight+frmShadow.Margins.Top+frmShadow.Margins.Bottom;
frmShadow.Left := Self.Left - frmShadow.Margins.Left;
frmShadow.Top := Self.Top - frmShadow.Margins.Top;
ShowWindow(frmShadow.Handle, SW_SHOWNA);
frmShadowEnabled := True;
end;
We must prepare the size and position of the shadow form in the FormShow procedure, as you noticed it, we are using the frmShadow.Margins to configure the shadow width and height, as well its left and top position. Following we will show the shadow form and tell that frmShadowEnabled is TRUE, that will be necessary for other procedures that will need to read frmShadow values, and if they try to read them before the shadow form is created, a read error will raise.
For example, we will also need to update the shadow form size when we resize our main form, so the FormResize procedure will be available as soon as the form is created, and even before the formShadow is created.
procedure TForm1.FormResize(Sender: TObject);
beginif WindowState = wsMaximized then
begin
ShowWindow(frmShadow.Handle, SW_HIDE);
end
else if WindowState = wsNormal then
begin
try
if frmShadowEnabled then
begin
if not IsWindowVisible(frmShadow.Handle) then
begin
ShowWindow(frmShadow.Handle, SW_SHOWNA);
end;
frmShadow.ClientWidth:=ClientWidth+frmShadow.Margins.Left+frmShadow.Margins.Right;
frmShadow.ClientHeight:=ClientHeight+frmShadow.Margins.Top+frmShadow.Margins.Bottom;
end;
except
end;
end
else if WindowState = wsMinimized then
begin
ShowWindow(frmShadow.Handle, SW_HIDE);
end;Repaint;
end;
This resize procedure will update the size of the shadow form too, but when calling IsWindowVisible function, if the shadow form is not created yet, it will fail, so we avoid that error by using the frmShadowEnabled as a boolean help.
Finally, we need to make sure our shadow form to stick firmly to our main form, so we will use the WMMove function which reacts to a window move message.
protected
procedure WMMove(var Msg: TWMMove); message WM_MOVE;…
procedure TForm1.WMMove(var Msg: TWMMove);
begin
inherited;if frmShadowEnabled then
begin
frmShadow.Left := Self.Left - frmShadow.Margins.Left;
frmShadow.Top := Self.Top - frmShadow.Margins.Top;
end;
end;
And that’ll do, making sure that the form shadow is ready, we can update its Top and Left values accordingly.
Of course there are other procedures that we will need to add, such us those required to resize a borderless window, drag it, etc. I have mentioned some of them in my other blog post about creating a Metro like window in Delphi. You can also see some of them in the source code of this tutorial.
Conclusion
This is a simple method to create a custom shadow for our windows, specially for borderless windows, giving it back a nice shadow instead of nothing or the very basic CS_DROPSHADOW style (those applied to contextual menus).
However, there are some disadvantages, like creating many more extra forms for other borderless forms in our application, but converting the shadow to a Delphi component will be enough.
Another issue that it presents, is that it flickers while resizing our window, not sure how to fix it, but at least it is working fine.
Finally
The source code and executable demo can be downloaded here.
Hope you liked it, and if you have more experience about this topic, feel free to leave a comment to improve this method I found by “write and try”.
<— Creating a Yosemite shadow for borderless form in Delphi [part 2]
0 comentarios:
Publicar un comentario