Delphi -
Managing Forms
Introduction to forms:
- a form is the Delphi equivalent of a window
- the "Main Form":
- an application can have only 1
"MainForm" which is the 1st form to be
created in the project source file using the
Application.CreateForm method
- thus to create a splash screen BEFORE the
main form is created, you must use
TSplashForm.Create(nil).
- NB. user cannot interact with
splash screen before
Application.run occurs, although
the user can see the splash
screen if "show" &
"update" are used.
- if this main form is freed/destroyed then the
application will terminate
- you cannot change which form is the main form at
runtime as this property is read only!
- a datamodule is a specialised non-visual
version of a form which can be used to maintain datasets
and their business rules, but note that if a form
references datasets in a datamodule, that datamodule must
already have been created, otherwise an access error will
result!!
Creating a form at run time:
- normally form creation is automatically performed by
Delphi at application startup via the project source code
(appname.dpr). However, this can cause application
startup time to be prolonged, esp. if form includes its
own database access.
- To get around this, use Project:Options to determine
which forms you wish to be auto-created at startup and
which forms will only be available to be manually created
when you so wish.
- You can then create a form using the following code:
- Application.CreateForm(FormClass,FormName);
- FormVariableName := FormClass.Create(Application); //this
syntax is preferred to the above which should only be used in DPR
file.
- Using manually created forms like this improves
performance BUT can cause 2 main types of problems for
the programmer:
- referencing an object in a form that has not yet
been created will cause an access violation &
crash your system (unless embedded in try..except
clause)
- multiple instances of a form may be inadvertently
be created causing unpredictable results
- To solve these problems, use any of the following
techniques to create a form manually:
- iterating through Application.Components:
- function TfrmMain.ShowForm(formName:
string) : boolean;
- {Determine whether a form has
been created or not}
- var i : integer;
- begin
- result := False;
- for i := 0 to
screen.formCount -1 do
- begin
- if
(screen.Forms[i].name =
formName) then
- begin result :=
True;Break;end;
- end;
- end;
- to show form then use:
- try
- if not IsForm('Form2') then Form2 :=
TForm2.Create(Application);
- Form2.show;
- except on E:exception do
Showmessage(E.message); end;
- this is simplified by using
gaTools.pas & calling:
- if
LoadForm(Form2,TForm2,'Form2')
then Form2.show;
- testing for form var = nil:
- Most forms have a var at the beginning of
the form, just after the class
declaration, eg:
- TForm2=class(TForm)
- ...
- end
- var Form2: TForm2;
- So you could test whether or not the form
was nil, eg
- if Form2<> nil then
Form2.show else Form2 :=
TForm2.Create(Application);
- However, the instance variable for Form2
is NOT set to nil upon the Form's
destruction, if the form was NOT
auto-created by the application, thus to
get around this problem, one can use the
form.tag property to store a pointer to
the form variable as follows:
- Mainform.OnCreate event:
- Form2 := nil;
//explicitly set all
non-auto-created form's
instances to nil
- Show form via:
- if Form2 = nil then
begin
- Form2 :=
TForm2.Create(Self);
- Form2.Tag :=
Longint(Addr(Form2));
- Form2.Show;
- end;
- On Form2's OnDestroy event:
- TForm2(Pointer(TForm2(Sender).Tag)^)
:= nil; //this
dereferences the pointer
to the form variable
& sets it to nil.
- MDI style:
- function
TfBaseApplication.CreateMDIForm(InstanceClass:
ComponentClass):TForm;
- var Reference: TfBaseMDIForm; i:
integer;
- begin
- for i:=0 to MDIChildCount-1 do begin
- if
MDIChildren[i].ClassName=InstanceClass.ClassName
then begin
- Result:=TfBaseMDIForm(MDIChildren[i]);
exit;end;
- end;
- try Screen.Cursor := crHourglass;
Application.CreateForm(InstanceClass,Reference);
Result:=Reference;
- finally Screen.Cursor :=
crDefault;end;
- end;
Closing a form:
- a user can close a form by activating the form.OnClose event by either:
- clicking on the X in the top right corner of the form
- pressing Ctrl-F4
- clicking a button or menu item which calls Form.close;
- in either case the default action is for the form to hide (ie. become
invisible) but still exist in memory and can be re-displayed by calling Form.Show
or Form.ShowModal.
- to remove a form from memory (ie. destroy it) then either:
- add this line to its OnClose event:
- action := caFree; //the default action is caHide; to
prevent a form from closing, use caNone;
- or call Form.free;
Splash screen at start up with
progress bar:
- Design a form with a progress bar on it (in this case its
Form5) then manually modify the project source file to
resemble:
- begin
- Application.Initialize;
- with TForm5.Create(nil) do // the spash
screen
- try
- ProgressBar1.Max := 100;
- Show; // show a splash screen contain
ProgressBar control
- Update; // force display of Form5
- Application.CreateForm(TForm1, Form1);
- ProgressBar1.StepBy(25);
- Application.CreateForm(TForm2, Form2);
- ProgressBar1.StepBy(25);
- Application.CreateForm(TForm3, Form3);
- ProgressBar1.StepBy(25);
- Application.CreateForm(TForm4, Form4);
- ProgressBar1.StepBy(25);
- finally Free; end;
- Application.Run;
- end.
Preventing more than one
instance of application from running:
- Option 1:
- Try this in the project source file:
- {usual stuff at the top of the project
source file
- var hwnd: Word;
- begin
- if hPrevInst = 0 then
- begin
- Application.CreateForm(TForm1, Form1);
- Application.Run;
- end
- else
- begin
- hwnd := FindWindow('TForm1', nil);
- if (not IsWindowVisible(hwnd)) then
- begin
- ShowWindow(hwnd, sw_ShowNormal);
- PostMessage(hwnd, wm_User, 0, 0);
- end;
- else
- SetWindowPos(hwnd, HWND_TOP,
0,0,0,0,SWP_NOSIZE or SWP_NOMOVE);
- end;
- end.
- In the form's PAS file add a message response
function for the wm_User message.
- {in the form declaration}
- public
- procedure WMUser(var msg: TMessage);
message wm_User;
- {in the implementation section}
- procedure TForm1.WMUser(var msg:
TMessage);
- begin
- Application.Restore;
- end;
- Option 2:
- in project source file:
- if hprevinst <> 0 then
- begin
- bringwindowtotop(findwindow('TForm1',
nil));
- halt;
- end;
- Application.CreateForm(TForm1, Form1);
- Application.Run;
Form
shapes:
- the default Window's form is rectangular, however there
are various ways in which you can create other shaped
forms:
- use Woll2Woll's 1st Class components
- Neil Rubenking has given code for a donut-shaped
splash form where clicking on hollow centre
activates programs visible there
- see Delphi Super Recall - DBK - #741
Status bar hints with multiple
forms:
- see Delphi Super Recall - DBK - #735 for technique using
same named status bar on each form then just creating one
DisplayHint procedure on the main form only.
-
Always
on Top:
- To use AlwaysOnTop windows like several toolbars etc,
then you can specify FormStyle := fsStayOnTop; But if you
want to switch your window to top and to normal, then
this flickers your form.A function that does it w/o
flickering:
- function PutOnTop(Form: TForm; ToTop:
boolean): boolean;
- var Status: Integer;
- begin
- if ToTop then Status := HWND_TOPMOST
else Status := HWND_NOTOPMOST;
- Result := SetWindowPos(Form.Handle,
Status, 0, 0, 0, 0, SWP_NOMOVE or
SWP_NOSIZE)
- end;
- You can use it like PutOnTop(Form1, true); and if you
want to normalize it, specify false as second param.
Making a secondary form the top
form instead of the main form:
- Try this in any secondary window that you DON'T want
dragging the program along:
- private {This goes in the form's type
declaration.}
- { Private declarations }
- procedure CreateParams(VAR Params:
TCreateParams); override;
- ...
- procedure TForm2.CreateParams(VAR Params:
TCreateParams);
- begin
- Inherited CreateParams(Params);
- Params.WndParent := GetDesktopWindow;
- end;
- By setting the form's parent window handle to the
desktop, you remove the link that would normally
force the whole application to come to the top
when this form comes to the top.
Making a form come to top of
system even when application is minimised:
- Check out API-function SetSysModalWindow
- Check out API-function BringWindowToTop
- If you just want a simple dialog with a bit of text, an
optional icon and some buttons, use MessageBox in
WinProcs and use MB_SYSTEMMODAL in the TextType
parameter.
- Otherewise, use the SetSysModalWindow() Windows API
function. The following code snippet demonstrates its
use. There can be only one system-modal window at a time,
and that handle is returned by SetSysModalWindow(). I
suggest that you save that return value and then reset it
when you are finished, as demonstrated below.
- procedure TForm1.Button1Click(Sender:
TObject);
- var x : word ;
- begin
- x := SetSysModalWindow(AboutBox.handle) ;
- AboutBox.showmodal ;
- SetSysModalWindow(x) ;
- end;
Making application invisible:
- make an app that has an icon in the notification area
with a popupmenu. However the application is still
visible on the taskbar. Using
Application.ShowMainForm:=False; is not enough.This
little bit of code works great.
- procedure TMainForm.FormCreate(Sender:
TObject);
- begin
- Application.OnMinimize:=AppMinimize;
- Application.OnRestore:=AppMinimize;
- Application.Minimize;
- AppMinimize(@Self);
- end;
- procedure TMainForm.AppMinimize(Sender:
TObject);
- begin
- ShowWindow(Application.Handle, SW_HIDE);
- end;
Form
sizing:
- you can set form size using:
- Form.height / Form.width properties
- Form.WindowState property (wsNormal, wsMaximized,
wsMinimized)
- but sometimes you can still have problems with the
Windows task bar so to work out its current height:
- Simple method to determine task bar height if
docked top/bottom:
- Screen.Height -
GetSystemMetrics(SM_CYMAXIMIZED)
- other way is to dig into AppBar messages. But
taskbar may be docked to left/right too, so you
must determine position first and then calculate.
Actually there is special SHAppBarMessage but it
is more complex. Other alternative is to hide
the taskbar.
- see also:
- controlling form sizing - DI Nov 1997 p64; Apr,
May1998
Hiding Windows TaskBar
temporarily:
- 1) First declare a variable of type HWND to store the
Window handle of the Windows 95 taskbar.
- TForm1 = class(TForm)
- ...
- private
- hTaskBar: HWND;
- ...
- end;
- 2) In your main form's OnCreate() event handler, place
some code that resembles:
- hTaskBar := FindWindow('Shell_TrayWnd', nil);
- ShowWindow(hTaskBar, SW_HIDE);
- 3) Finally, in your main form's OnDestroy() event
handler, code something like:
- ShowWindow(hTaskBar, SW_SHOW);