views:

341

answers:

3

Hello,

My Delphi 2010 application has a number of non-modal forms that are created and owned by the mainform. One of these forms has a formclose procedure that pops up a dialog asking the user if they want to save changes. If the user closes the mainform, the "owned" form's FormClose procedure is called, however the dialog is not shown, and the user has no chance to save.

Any suggestions? I can see the procedure is being called in the debugger, but it seems to just skip the dialog. Same thing happens with a showmessage. Does the owner form somehow override the actual showing of these dialogs?

Thanks

Rusty

+2  A: 

That kind of thing should go in the OnCloseQuery event. Set CanClose to false in the handler to abort the closing (which is more or less standard: in these situations, Yes, No and Cancel are the usual answers, with Cancel aborting the closing process).

Michael Madsen
thanks, that seems to work. Previously I was using the FormClose procedure but changing the "Action" (TCloseAction) parameter accordingly. Apparently this doesn't always work, or else I dont understand how to use it correctly!
Rusty
There must be something else in your code because neither OnClose nor OnCloseQuery is called automatically for owned forms just by closing the main form.
TOndrej
Correct - in the mainform.formclose procedure, I explicity say OwnedForm.Close to trigger its FormClose... more specifically i do something like : for i := 0 to ComponentCount-1 do if Components[i] is TForm then (Components[i] as TForm).Close;
Rusty
+1  A: 

When the main form is closed then the application terminates which frees the main form which in turn frees the forms owned by it. The owned forms are not closed, just freed, therefore their OnClose event is normally not triggered at all.

If you see ShowMessage being called from the owned form's OnClose event but the dialog doesn't show up it's probably because the application is already terminated and no longer processing messages. This means that the owned form's OnClose event is triggered by somewhere in your own code but too late.

One way to reproduce this behaviour is to post WM_CLOSE message to the owned form from the main form's OnClose event. The message is then processed by the owned form at a later moment when the application is already terminated any attempt to call ShowMessage or any modal form has no effect anymore.

I agree with Michael that OnCloseQuery is better suited for the purpose of displaying a prompt to the user. Unfortunately this alone doesn't help since the owned forms are being freed not closed. You have to call their OnCloseQuery event manually, for example:

procedure TFormMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  I: Integer;
begin
  CanClose := False;
  for I := 0 to ComponentCount - 1 do
    if Components[I] is TCustomForm then
      if not TCustomForm(Components[I]).CloseQuery then
        Exit;
  CanClose := True; // or another check if the main form can be closed, too
end;
TOndrej
this is essentially what I was doing in the first place to get the ownedform.formclose to trigger. I guess the application will allow the messages if called from the FormCloseQuery rather than the FormClose.
Rusty
When you're closing any form, first its OnCloseQuery is called, then (if OnCloseQuery allowed closing) OnClose, and then (if it's the main form and OnClose returned anything but caNone) the application is terminated (see TCustomForm.Close). Whether you use OnCloseQuery or OnClose of the main form, the application should still be processing messages at that point. There must have been something else in your code; either you used another event or a different technique to close the owned forms after the application has been terminated.
TOndrej
A: 

I recently ran into something along these lines. I found that simply adding the code:

if not Visible then
  Show;
BringToFront;

right before the save changes dialog is displayed ends all confusion. The parent form is displayed if its not visible, and brought upward in zorder to the front of the pile, then on top of that is displayed the dialog.

skamradt