Delphi closing all forms

Please help,

I am trying to close all mdi froms inside a main form (except the main form)
with the following code:

////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
Procedure TmainForm.CloseAllForms;
var
 CIndex: Integer;
 FIndex: Integer;
begin
  With MainForm do
  for fIndex := MdiChildCount - 1 downto 0 do
  begin
    for CIndex := 0 to MDIChildren[fIndex].ComponentCount - 1 do
    begin
      if ((MDIChildren[fIndex].Components[CIndex] is TQuery) or
          (MDIChildren[fIndex].Components[CIndex] is TTable)) then
      begin
        if TTable(MDIChildren[fIndex].Components[CIndex]).State in [dsEdit,
dsInsert]  then
        try
          TTable(MDIChildren[fIndex].Components[CIndex]).Cancel;
        except
        end;
      end;
    end;
    MDIChildren[fIndex].Close;
  end;

  // i need the below code because sometimes the main form is disabled and i
cannot do anything on it
  application.MainForm.formstyle := fsNormal;
  application.ProcessMessages;
  application.MainForm.formstyle := fsMDIForm;
  application.ProcessMessages;
end;
////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
The mdi forms are created by looping through all forms on screen (i.e.
screen.formcount)
and if the form is found (by checking its class) it calls bringtofront to
show the form it else it creates it;
on the OnClose event of all mdi forms there is an Action := caFree;


The mdi forms may have modal forms which are called as follows:
////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
var
    Form: tfrmmyform;
begin
  Form := tfrmmyform.Create(Self);
  try
    form.showmodal;
  finally
    form.free;
  end;
end;
////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

The problem is:
1) When I try to reopen the mdi-form again (which was previously closed by
the MDIChildren[fIndex].Close), the form is not shown
     as it is still found on the Screen.Forms, and when bringtofront is
called nothing happens.
2)  When I close the main form (no mdi forms on screen - just the main form)
the code passes to the finally part of the modal form,
      causing an access violation.


Please help urgently.  if you need more information pls tell

Regards
Joseph Pellicano

pellj001Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

mocartsCommented:
first, you may want to alter this code to work only with TDataSet (or instead place it in OnClose event of MIDChild). (if component is TQuery you may get AccessViolation as it is not the same as TTable):
...
for CIndex := 0 to MDIChildren[fIndex].ComponentCount - 1 do
begin
  if ((MDIChildren[fIndex].Components[CIndex] is TDataSet) then
  begin
    if TDataSet(MDIChildren[fIndex].Components[CIndex]).State in [dsEdit,
dsInsert]  then
      try
        TDataSet(MDIChildren[fIndex].Components[CIndex]).Cancel;
      except
      end;
  end;
end;

from which place (form, subform, modal or not) you call that CloseAllForms method?
mo.
0
pellj001Author Commented:
I am calling the CloseAllForms from the main form (on a timer event)

Joseph Pellicano
0
mocartsCommented:
to show mdichildren modal you set form style to fsNormal? in this case form will not be in MDIChildren collection.

you may use this code to close all forms except main:

procedure TForm1.Timer1Timer(Sender: TObject);
var
  i: integer;
begin
  for i := Screen.FormCount -1 downto 0 do
  begin
    if Screen.Forms[i] <> Self then
      Screen.Forms[i].Close;
  end;
end;

mo.
0
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

mocartsCommented:
TForm1 in my case is TmainForm in your case.
0
pellj001Author Commented:
Mdi modal forms are all set to fsNormal
While mdi non modal forms are set to fsMdiChild
TMainForm is set to fsMdiForm

When you call Screen.Forms[i].close the form in question is not freed.  If you check screen.form count after calling Screen.Forms[i].close the result will be the same.

Also when calling Screen.Forms[i].close for a mdi-form the code passes through the formclose event of that form.  However when calling Screen.Forms[i].close for a modal form the code does not pass from the formclose event.

My problem still applies.

0
mocartsCommented:
to trigger OnClose event for modal form you should set ModalResult to some value other than zero:

procedure TForm1.Timer1Timer(Sender: TObject);
var
 i: integer;
begin
 Timer1.Enabled := FAlse;
 for i := Screen.FormCount -1 downto 0 do
 begin
   if Screen.Forms[i] <> Self then
     if fsModal in Screen.Forms[i].FormState then
       Screen.Forms[i].ModalResult := mrCancel
     else
       Screen.Forms[i].Close;
 end;
  Application.ProcessMessages;
  ShowMessage(inttostr(Screen.FormCount));
end;

and form count will be 1 only after modal form freeing:

procedure TForm1.Button2Click(Sender: TObject);
begin
  with TForm2.Create(Self) do
  try
    ShowModal;
  finally
    Free;
  end;
  ShowMessage(inttostr(Screen.FormCount));
end;

0
serakCommented:
Hi, I had the same problem until I included this!

1. In every MdiChild.Close event include these two lines:

  Action := caFree;
  Release;

2. And in your MainForm you want to include this Notification:

TfrmMain = class(TForm)
  private
    procedure Notification(AComponent:TComponent; Operation:TOperation);override;
  end;

procedure TfrmMain.Notification(AComponent:TComponent; Operation:TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation=opRemove) then begin
    // If you have control of the subforms:
    if      (AComponent=frmSub1) then frmSub1:=Nil
    else if (AComponent=frmSub2) then frmSub2:=Nil
    ...
    //OR this one:
    AComponent:=Nil;
  end;
end;

This should clean things up.
0
pellj001Author Commented:
to serak;
Can you give me the code for the closing of all forms.
I am still receiving access violations.

Joseph
0
mocartsCommented:
if you use Release then you must call AddRef before ShowModal or don't use Free, as Release, if reference counter is zero, frees form.
Can you tell us, Joseph, what you exactly want to do with your app. why this onTimer event closing all forms?
mo.
0
pellj001Author Commented:
I tell you what am I trying to do.
I have a mainform (fsMdiForm) from which mdiChild forms can be called from the main menu.  An mdiChild may call other modal forms.

When a specified timer is reached, I am loading a form so that the user logs on again.  After he logs on, if the user is not the same I am closing (or trying to close) all the forms on the main form.

mo, I did not understand your last comment.  I do not know what Addref is?

Joseph
0
mocartsCommented:
Sorry, I mixed Release with _Release which takes care of reference counting..

procedure TForm1.Timer1Timer(Sender: TObject);
var
  i: integer;
begin
  Timer1.Enabled := False;
  with TfrmLogin.Create(self) do
  try
    ShowModal;
    BringToFront; // to front of other modal form if any

    if not LoginOk then // login filed (LoginOk property of TfrmLogin)
    // close all forms (if loginOk we can leave forms opened)
    for i := Screen.FormCount -1 downto 0 do
    begin
      if Screen.Forms[i] <> Self then
        if fsModal in Screen.Forms[i].FormState then
          Screen.Forms[i].ModalResult := mrCancel
        else
          Screen.Forms[i].Close;
    end;
  finally
    free;
  end;
end;

and your main problem - when showing your form modal create form with no owner:

var
   Form: tfrmmyform;
begin
 Form := tfrmmyform.Create(nil); // to turn off auto freeing by owner
 try
   form.showmodal;
 finally
   form.free;
 end;
end;

wbr, mo.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
pellj001Author Commented:
that create(nil) seemed to solve my problem.

Thank you very much mocarts.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.