Re: does FormClose have some special magic?



swansnow wrote:
I have a login screen. If the username and password are ok, then I want
that screen to close, and the splash/loading screen to appear.

You should edit your DPR file so it looks like this:

var
  PasswordDlg: TPasswordDlg;
begin
  Application.Initialize;
  PasswordDlg := TPasswordDlg.Create(nil);
  try
    if PasswordDlg.ShowModal <> mrOK then exit;
  finally
    PasswordDlg.Free;
  end;
  Application.CreateForm(TMainAppForm, MainAppForm);
  Application.Run;
end;

Don't make your password-checking dialog responsible for boot-strapping the rest of the program.

With the above code, you should not have to handle the OnClose event. You can handle the OnCloseQuery event like this:

procedure TPasswordDlg.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if ModalResult = mrOK then begin
// User pressed OK; check the password
CanClose := PasswordIsCorrect(PasswordEdit.Text);
end;
end;


You won't need event handlers for the OK and Cancel buttons anymore. Just set the ModalResult properties to mrOK and mrCancel, respectively, and Delphi will take care of the rest. (Pressing a button with a non-zero ModalResult property automatically tries to close the form. If closing is successful, that button's ModalResult property ends up being the return value of the modal form's ShowModal method.)

At the end of the password validation code I have
form1.show;  {loading screen}
close;   {causes the application to terminate}

Here's FormClose:

procedure TPasswordDlg.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  passwordDlg.Release;  {global variable for the password screen}
  PasswordDlg := nil;
end;

Two issues with that:

1. Do not reference the form's global variable from within its own event handlers. The form shouldn't need to know the names of the variables that hold references to it. It makes for maintenance headaches if there's ever more than one instance of a form, or if things get renamed.

2. Instead of calling Release, you can just set the Action parameter to caFree. When the form is ready, it will destroy itself. (The default value for Action is caHide, so the form remains available for re-use.)

Now I have a cancel button whose handler calls application.terminate,
but otherwise I don't every try to terminate.

Now, here's where the "magic" comes in.

I created a procedure *with the same code*  and the application doesn't
terminate. The loading screen appears the way it should.

Here it is:

procedure TPasswordDlg.closeDontTerminate;
begin
    PasswordDlg.release;
    PasswordDlg := nil;
end;


Why? My best guess is that the dpr only creates the password form, and when it closes, that's it, the app terminates. Application.CreateForm(TPasswordDlg, PasswordDlg);

The first form to finish being created that way is designated as the application's "main form." When the main form closes, the application is finished, no matter what you do afterward.


However, when I create the loading form in the password validation
code, I do
Application.CreateForm(TForm1, Form1);

So shouldn't that register that there is another form to the
application, and thereby prevent it from terminating when the first
form closes?

No. The application doesn't care how many forms you have. It only cares about the first one.


Never call CreateForm more than once. Reserve it for the main form, and call it only in the DPR file, where the IDE can see it and control it. Create all other forms the same way you create any other kind of object: Call its constructor.

Dlg := TPasswordDlg.Create(nil);

--
Rob
.