Nicht-modale Formulare haben's in sich. Nach dem Schließen werden sie standardmäßig nicht zerstört, sondern nur versteckt (Visible := false). Hierzu muss man keinen OnClose-Handler des zu schließenden Formulars schreiben. Vorteil ist, dass das Formular bei der nächsten Verwendung genauso wiederkommt, wie es geschlossen war. Nachteil ist, dass der "Schrott" der letzten Eingabe noch drinnen steht, und dass das Formular immer noch Speicher frisst.
Wenn man das Formular beim Schließen (Klick auf dem 'x') zerstören will, braucht man das OnClose-Ereignis, in dem man durch den Parameter Action festlegen kann, "wie" das Formular geschlossen wird. Wiegesagt, default ist "caHide", aber zum Zerstören braucht man "caFree". Ein Problem entsteht aber, wenn man im aufrufenden Formular noch eine Variable für das geschlossene Formular hat, so wie du mit "f". Das Formular existiert nun nicht mehr. Wenn man das Formular "f" ein zweites Mal öffnet, ergibt die Prüfung "Assigned(f)" true, denn f wurde beim Zerstören nicht automatisch auf nil gesetzt. Ergebnis: Crash.
Die Lösung ist zum Beispiel, dass man das aufrufende Formular entscheiden lässt, ob f zerstört wird. Das heißt, der OnClose-Event-Handler wird nicht von TForm2 bereitgestellt, sondern von TForm1. Da TForm1 die Variable "f" kennt, kann es sie auf nil setzen;
Code: Alles auswählen
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Assigned(f) then begin
ShowMessage('Create');
f:=TForm2.Create(Self);
f.OnClose := @Form2Close;
end else begin
ShowMessage('no Create');
end;
f.Show;
end;
procedure TForm1.Form2Close(Sender: TObject; var CloseAction: TCloseAction);
begin
CloseAction := caFree;
f := nil;
end;
Unschön ist an deiner Lösung auch, dass f: TForm2 in der Deklaration von TForm1 steht. Das bewirkt, dass Unit2 in die Uses-Zeile des Interface-Teils von Unit1 aufgenommen werden muss. In dem Beispiel geht es noch, aber wenn TForm2 auch etwas von TForm1 braucht, dann muss auch Unit1 in den Interfaceteil von Unit2, und schon hast du einen schönen Zirkelbezug.
Besser ist nur so wenig wie mögich zu deklarieren. In der Deklaration von TForm1 wird eigentlich nur benötigt, dass es noch ein Formular gibt (f), auf das an mehreren stellen zugegriffen werden muss. Da reicht es, f als TForm zu deklarieren, und Unit2 kann vom Uses des Interface-Teils in das des Implementation-Teils wandern. Dort ist es ausreichend, um dann f als TForm2.Create erzeugen zu können:
Code: Alles auswählen
interface
uses
...; // ohne Unit2;
type
{ TForm1 }
TForm1 = class(TForm)
....
private
f:TForm; // nicht TForm2
....
end;
implementation
uses
Unit2; // war früher in interface
procedure TForm1.Button1Click(Sender: TObject);
begin
...
f:=TForm2.Create(Self); // f als TForm2 erzeugen, obwohl es nur als TForm deklariert ist
....