Author: Dmitri Papichev
How to manage the application's non-modal forms properly - to make sure that only
one instance of a form is present at any given time, like it is done automatically
for modal forms?
Answer:
When you have a modal form, you create, display and destroy it usually in a single
uninterrupted sequence of code instructions, therefore it is easy to ensure that
your form variable is equal to NIL after the form is expired, so you easily
eliminate the risk of creating the same form twice or, even worse, attempting to
access non-existing form when the only form's remains you actually have is the form
variable with non-NIL value in it.
The problem with non-modal forms is that you usually create and display them in one
place, then do whatever you want with your application (and not only that form),
and order them to destroy themselves when they are no longer needed at any time -
you cannot say beforehand where and when that will happen.
It is natural to close and free such a non-modal form from itself, but where to put
clean-up instructions then? Without them, the application cannot know whether the
form is alive - it relies merely on the form variable value (is it equal to NIL?)
to determine if the form is still around. It is possible to introduce some
application-level flags to keep track of the forms presence, but it is not an
elegant solution.
Fortunately, as almost always with Delphi, there is a clear way to solve the
problem.
Suppose you have frmNonModal as a form variable of TfrmNonModal class.
The procedure is simple:
1. Add the following OnDestroy event handler to the form:
1 procedure TfrmNonModal.FormDestroy(Sender: TObject);
2 begin
3 frmNonModal := nil;
4 inherited;
5 end; {TfrmNonModal.FormDestroy}
Note that you cannot substitute frmNonModal with Self, though it seems to be the
same from the first glance. The difference is that Self points to the object itself
(and we don't want to nullify it prematurely), and frmNonModal is just an external
(to the object) variable, which points to the object. By setting this variable to
NIL we do not affect any internal functionality of the object, but rather just
disconnect its link to the outer world. In our case this is exactly what we need,
as we plan to check this variable when creating the form, as shown below.
2. Add the following OnClose event handler to the form:
6
7 procedure TfrmNonModal.FormClose(Sender: TObject; var Action: TCloseAction);
8 begin
9 inherited;
10 Action := caFree;
11 end; {TfrmNonModal.FormClose}
This is needed to instruct our form to free itself when we close it. Note that by
default Action is caHide, i.e. your form is just being closed (hidden), but not
destroyed after calling TForm.Close.
3. Bring your form to life as follows, for example in the menu item's OnClick
event handler on your main form (of TfrmMain class):
12
13 procedure TfrmMain.itmNonModalFormClick(Sender: TObject);
14 begin
15 if Assigned(frmNonModal) then
16 begin
17 {if the form already exists, just bring it to the front}
18 frmNonModal.Show;
19 frmNonModal.WindowState := wsNormal;
20 frmNonModal.BringToFront;
21 end
22 else
23 begin
24 {create the form if it does not exist}
25 frmNonModal := TfrmNonModal.Create(Application);
26 try
27 {display as non-modal form}
28 frmNonModal.Show;
29 except
30 {clean up the form and form variable if unsuccessful}
31 FreeAndNil(frmNonModal);
32 end; {try}
33 end; {if}
34 end; {TfrmMain.itmNonModalFormClick}
That's it. Now your application always knows whether your non-modal form is present (created), and manages to allow not more than a single instance of such form at a time.
|