Ok, now we know how to add more forms to a Delphi (SDI) project, and even how to call a modal form, let's see how to call some of those forms in a non-modal fasion...
A Non-Modal Form
If the secondary form needs to be non-modal, use Show instead of ShowModal. We can't use exactly similar code because the form would show briefly, then disappear, because Release is called right after showing the form. To correct this problem use the code shown below:
procedure TfrmMain.cmdAddNewCustomerClick(Sender: TObject);
var
f:TfrmAddNewCustomer ;
begin
f := TfrmAddNewCustomer.Create(Self);
f.Show ;
end;
|
Then in the FormClose event for the Add New Customer form add the following (Action caFree frees up all resources for the form and destroys it):
procedure TfrmAddNewCustomer.FormClose(Sender: TObject);
var
Action: TCloseAction);
begin
Action := caFree ;
end;
|
Whoops, there is a slight problem with the code! Can you see the problem? Since the secondary form is non-modal the user can go back to the main form and press the Add button again. The program tries to create another Add New Customer form with unfortunate results if you have not planned for it! To prevent this from happening first check to see if the form object already exists. If so, simply show it; if not, create the form and then show it.
There are various ways to test for an object's existence, for example:
procedure TfrmMain.cmdAddNewCustomerClick(Sender: TObject);
var
f:TfrmAddNewCustomer ;
iFound,
i:Integer ;
begin
iFound := -1;
for i := 0 to Screen.FormCount -1 do
if Screen.Forms[i] is TfrmAddNewCustomer then
iFound := i;
if iFound >= 0 then
begin
ShowMessage('Add Customer form already created, will now show it') ;
Screen.Forms[iFound].Show ;
end
else
begin
ShowMessage('Add New Customer form not found, creating...') ;
f := TfrmAddNewCustomer.Create(Self) ;
f.Show ;
end;
end;
|
These methods give your program much better control of resources than auto-created forms, with very little hand-coding. There is a slight performance trade-off with dynamically created forms, but a delay is unlikely to be perceptible unless the called forms are very complex and/or your user has an unreasonably slow machine.
Caution: When defining forms for dynamic invocation you should be aware of the need to avoid any named reference to the properties of the object whose variable is declared in the interface section. That variable will never be assigned because the calling form uses its own local variable to invoke it. For example:
procedure TfrmAddNewCustomer.FormCreate(Sender: TObject);
begin
frmAddNewCustomer.Top := Top + 12 ;
end;
|
Using our example, the reference to the variable frmAddNewCustomer would cause an access violation. The code below demonstrates how you need to work with properties of the secondary form
procedure TfrmAddNewCustomer.FormCreate(Sender: TObject);
begin
Top := Top + 12 ;
end;
|
If for some reason you need an explicit reference to the form object, use the identifier Self in front of the property or method.
procedure TfrmAddNewCustomer.FormCreate(Sender: TObject);
begin
Self.Top := Self.Top + 12 ;
end;
|
The variable in the interface section of can be safely removed if the form is intended only for dynamic invocation. It is scarcely worth doing, though, since it will be optimized out when the project is compiled.
For completeness, here is a method for showing a modeless form.
procedure TfrmMain.cmdAddNewCustomerClick(Sender: TObject);
var
f:TfrmAddNewCustomer ;
begin
with TfrmAddNewCustomer.Create(Self) do
begin
Show;
end;
end;
|
Once you are finished using the form it can be destroyed by using the following code:
procedure TfrmMain.cmdCloseFormClick(Sender: TObject);
var
f:TForm;
begin
f := TForm(FindComponent('frmAddNewCustomer')) ;
if f nil then
f.Release
else
ShowMessage('Failed to find it') ;
end;
|
All set. Ready to code ... read on and learn about some more from_the_battle_field situations...
Next page > Tidy Habits; Right Place, Right Time > Page 1, 2, 3, 4
A Beginner's Guide to Delphi Programming: Next Chapter >>
>>
Communicating Between Forms