Animate windows as they are opened and closed #46
Ever wandered how to animate a window that's opening or closing in the same way that Windows uses when minimizing applications to the task bar?
We do it using the following Windows API function, defined in
function DrawAnimatedRects(hwnd: HWND; idAni: Integer; const lprcFrom, lprcTo: TRect): BOOL; stdcall;
This function animates the window whose handle is hwnd. It is animated from the rectangle specified in lprcFrom to the rectangle specified by lprcTo. These rectangles are usually set to the the bounds of the source and destination windows, in screen co-ordinates. If you want the window's caption to be animated as when minizing to the task bar we pass IDANI_CAPTION as the idAni parameter. Other values can be passed, but we don't deal with them here – experiment yourself!
Create an application that has a button that, when clicked, toggles a non-modal form open and closed. We will animate the dialog form when opened to make it appear to "grow" from the button and reverse the process when the dialog is closed.
Create a new application, name the main form MainForm and save it
FmMain.pas. Add a TButton to MainForm
with caption "Show / Hide Dialog". Now add a new form named
DialogForm and save it as
FmDialog.pas. Make sure
DialogForm is auto-created in the project file.
The main form requires three event handlers: an OnClick handler
for the button and OnClose and OnShow handlers for the
FmDialog to the uses clause of the main form's
implementation section then implement the event handlers as follows:
procedure TMainForm.Button1Click(Sender: TObject); begin // Toggle visibility of DialogForm. DialogForm's OnShow / OnHide event // handlers take care of animation DialogForm.Visible := not DialogForm.Visible; end; procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); begin // Close dialog (if open) with animation before app closes DialogForm.Hide; end; procedure TMainForm.FormShow(Sender: TObject); begin // Set SourceCtrl here rather than FormCreate to ensure DialogForm created DialogForm.SourceCtrl := Button1; end;
In the FormShow event handler we set a custom property of the dialog box form that holds a reference to the button – this is so the dialog box knows where to animate the dialog to and from, as we'll see in a moment.
In the dialog box unit add handlers for the form's OnShow and OnHide events and then complete the dialog form's interface section as follows:
type // Ensure this form is auto-created in project file TDialogForm = class(TForm) procedure FormHide(Sender: TObject); procedure FormShow(Sender: TObject); private fSourceCtrl: TControl; function GetSourceCtrlBounds: TRect; procedure AnimateDialog(const Src, Dest: TRect); public property SourceCtrl: TControl read fSourceCtrl write fSourceCtrl; end; var DialogForm: TDialogForm;
Implement the methods of TDialogForm as follows:
procedure TDialogForm.AnimateDialog(const Src, Dest: TRect); begin DrawAnimatedRects(Handle, IDANI_CAPTION, Src, Dest); end; procedure TDialogForm.FormHide(Sender: TObject); begin if WindowState <> wsMinimized then AnimateDialog(BoundsRect, GetSourceCtrlBounds); end; procedure TDialogForm.FormShow(Sender: TObject); begin if WindowState <> wsMinimized then AnimateDialog(GetSourceCtrlBounds, BoundsRect); end; function TDialogForm.GetSourceCtrlBounds: TRect; begin Assert(Assigned(fSourceCtrl)); Result.TopLeft := fSourceCtrl.ClientToScreen(Point(0, 0)); Result.BottomRight := fSourceCtrl.ClientToScreen( Point(fSourceCtrl.Width, fSourceCtrl.Height) ); end;
AnimateDialog calls DrawAnimatedRects to perform the animation. FormShow and FormHide first check the dialog isn't minimized then call AnimataDialog, passing the bounding rectangle of the dialog and the button as the appropriate start and end points of the animation. GetSourceCtrlBounds simply calculates the bounding rectangle of the main form's button in screen co-ordinates. It uses the SourceCtrl property to get a reference to the button.