Mehrere geöffnete Formulare

Für Fragen von Einsteigern und Programmieranfängern...
wp_xyz
Beiträge: 3632
Registriert: Fr 8. Apr 2011, 09:01

Re: Mehrere geöffnete Formulare

Beitrag von wp_xyz »

Bernie110 hat geschrieben:
Do 18. Feb 2021, 13:02
Die Lösung war das Formular := Nil bei Close zusetzten.
Ich dachte CaFree würde ausreichen...
Das mag zwar momentan funktionieren, ist aber eine Zeitbombe. Denn wenn du im Code, der für die ganze Klasse implementiert ist, also für alle Instanzen, egal wie sie heißen, eine bestimmte Instanz dieser Klasse mit dem Variablennamen (hier "Formular") ansprichst, dann ist es nur Zufall, wenn es gut geht. Was du willst, ist die Variable der aktuellen Instanz, die irgendwo gespeichert ist, auf nil zu setzen. Das geht nur, wie ich oben gezeigt habe, wenn die Klasse, die die Instanz erzeugt hat - denn sie kennt die Variable -, das OnClose implementiert und dort diese Variable auf nil setzt.

Aber ich seile mich nun ab, diese Diskussion hatten wir doch vor nicht allzulanger Zeit schon mal

Bernie110
Beiträge: 120
Registriert: Mo 10. Feb 2020, 17:43

Re: Mehrere geöffnete Formulare

Beitrag von Bernie110 »

wp_xyz hat geschrieben:
Do 18. Feb 2021, 14:13
Bernie110 hat geschrieben:
Do 18. Feb 2021, 13:02
Die Lösung war das Formular := Nil bei Close zusetzten.
Ich dachte CaFree würde ausreichen...
Das mag zwar momentan funktionieren, ist aber eine Zeitbombe. Denn wenn du im Code, der für die ganze Klasse implementiert ist, also für alle Instanzen, egal wie sie heißen, eine bestimmte Instanz dieser Klasse mit dem Variablennamen (hier "Formular") ansprichst, dann ist es nur Zufall, wenn es gut geht. Was du willst, ist die Variable der aktuellen Instanz, die irgendwo gespeichert ist, auf nil zu setzen. Das geht nur, wie ich oben gezeigt habe, wenn die Klasse, die die Instanz erzeugt hat - denn sie kennt die Variable -, das OnClose implementiert und dort diese Variable auf nil setzt.

Aber ich seile mich nun ab, diese Diskussion hatten wir doch vor nicht allzulanger Zeit schon mal
Oooohhhhhh herzlichen Dank dass du das nochmals geschrieben hast.
Verstehe dass du am verzeifeln bist. :?
Ich hatte deinen ersten Beitrag wohl irgendwie falsch gelesen/verstanden/überlesen
Bitte verzeih!

Alles klar. Man muss es aus dem Formular steuern welches es auch erstellt und dieses OnClose ist eigentlich unnötig.
also so

Code: Alles auswählen

procedure TFrm_A_MAIN_HAUPTMENU.MenuItem18Click(Sender: TObject);
var
LI: TListItem;
begin
   //Frm_PERSONAL_STAMM_UEBERSICHT
 if not Assigned(Frm_PERSONAL_STAMM_UEBERSICHT) then
    begin
       Application.CreateForm(TFrm_PERSONAL_STAMM_UEBERSICHT  , Frm_PERSONAL_STAMM_UEBERSICHT );

       Frm_PERSONAL_STAMM_UEBERSICHT.PopupParent := Frm_A_MAIN_HAUPTMENU;
       Frm_PERSONAL_STAMM_UEBERSICHT.left   := left + FENSTER_PANEL.left;
       Frm_PERSONAL_STAMM_UEBERSICHT.Top    := Top +  FENSTER_PANEL.top;
       Frm_PERSONAL_STAMM_UEBERSICHT.width  := FENSTER_PANEL.width;
       Frm_PERSONAL_STAMM_UEBERSICHT.Height := FENSTER_PANEL.Height;
       Frm_PERSONAL_STAMM_UEBERSICHT.OnClose := @Frm_PERSONAL_STAMM_UEBERSICHTClose;

       Frm_PERSONAL_STAMM_UEBERSICHT.Show;
       LI := ListView1.Items.Add;
       LI.Caption := 'PERSONAL ÜBERSICHT';
       LI.SubItems.Add('Frm_PERSONAL_STAMM_UEBERSICHT');
       LI.ImageIndex := 2;

    end else
    begin
     Frm_PERSONAL_STAMM_UEBERSICHT.FormStyle:=fsStayOnTop;
     Frm_PERSONAL_STAMM_UEBERSICHT.FormStyle:=fsNormal;

    end;


end;
procedure TFrm_A_MAIN_HAUPTMENU.Frm_PERSONAL_STAMM_UEBERSICHTClose(Sender: TObject; var CloseAction: TCloseAction);
var
i : Integer;
begin
  CloseAction := caFree;
  Frm_PERSONAL_STAMM_UEBERSICHT := nil;

  for i := 0 to ListView1.Items.Count - 1 do
    if ListView1.Items[i].Caption  = 'PERSONAL ÜBERSICHT' then
      begin
        ListView1.Items.Delete(i);
        break;
      end;
end;  
Puhhh schwierige Geburt.. Das ist auch viel einfacher und übersichtlicher finde ich.
@Frm_PERSONAL_STAMM_UEBERSICHTClose Das man die Procedure so aufrufen kann, konnte ich mir beim besten Willen nicht vorstellen....
Danke Danke Danke Danke Danke

Lg Bernie

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 840
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Mehrere geöffnete Formulare

Beitrag von fliegermichl »

Es ist egal, ob das OnClose seinen Wert per Zuweisung im Quelltext oder im Objektinspektor bekommt.
Das Problem ist die Variable.
Nehmen wir an, du hast ein Formular erzeugt mit dem Namen "MyForm" und dieses wird auch automatisch erzeugt.
Dann legt Lazarus automatisch eine Klasse TMyForm und eine Variable MyForm : TMyForm an.
In MyForm habe ich einen Button und ein OnClose definiert.

Code: Alles auswählen

type
 TMyForm = class ( TForm )
  Button1 : TButton;
  procedure Button1Click(Sender : TObject);
  procedure FormClose(Sender : TObject; var CloseAction : TCloseAction);
 private
 public
 end;

var MyForm : TMyForm;
      f1, f2 : TMyForm;

implementation

procedure TMyForm.FormClose(Sender : TObject; var CloseAction : TCloseAction);
begin
 CloseAction := caFree;
 MyForm := nil;
end;

procedure TMyForm.Button1Click(Sender : TObject);
begin
 f1  := TMyForm.Create(nil);
 f2 := TMyForm.Create(nil);
 f1.Show;
 f2.Show;
end;
Wenn du jetzt das X anklickst, wird das Formular geschlossen und die Variable MyForm auf nil gesetzt.
Wird f1 geschlossen, so wird ebenfalls das Formular geschlossen und der Speicher freigegeben und die Variable MyForm auf nil gesetzt.
Aber NICHT f1 bzw. f2. Die beiden zeigen munter weiter auf den Speicher wo früher mal die zweite bzw dritte Instanz der Klasse TMyForm lebte.
greifst du jetzt irgendwo auf f1.xxx zu, kracht es.

charlytango
Beiträge: 333
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Mehrere geöffnete Formulare

Beitrag von charlytango »

@fliegermichl:
Dann verstehe ich aber so einiges nicht.

Code: Alles auswählen

procedure TMyForm.FormClose(Sender : TObject; var CloseAction : TCloseAction);
begin
 CloseAction := caFree;
 MyForm := nil;    <---- ????
end;
egal ob nun f1 oder f2 geschlossen wird (unter der Voraussetzung dass die FormularInstanz MyForm zuerstört wurde, also nicht mehr existiert) es muss immer krachen denn ein Zugriff auf MyForm muss ins Leere führen.

ungetestet wäre da vielleicht

Code: Alles auswählen

procedure TMyForm.FormClose(Sender : TObject; var CloseAction : TCloseAction);
begin
 CloseAction := caFree;
 self := nil; 
end;
allgemeiner, aber wohl auch nicht zielführend, denn eigentlich müsste die CloseAction auf caFree gesetzt ja reichen.

Denn im Close der TCustomForm

Code: Alles auswählen

TCustomForm.Close
      .....
      case CloseAction of
        caHide: Hide;
        caMinimize: WindowState := wsMinimized;
        caFree:
          begin
            // if form is MainForm, then terminate the application
            // the owner of the MainForm is the application,
            // so the Application will take care of free-ing the form
            // and Release is not necessary
            if IsMainForm then
              Application.Terminate
            else
              Release;
          end;  
       ......
wird das Formular bei einem caFree ja "released"
was letztlich ein Freigeben des Formulars aus Appliation-Ebene hervorruft.

Code: Alles auswählen

procedure TCustomForm.Release;
begin
  if Application <> nil then
    Application.ReleaseComponent(Self)   <----
  else
    Free;
end; 
https://lazarus-ccr.sourceforge.io/docs ... onent.html


Vermutlich aus alter Gewohnheit der Delphi-Zeiten und weil ich mich besser fühle verwende ich immer freeandnil() um ein geschlossenes Formular auf nil zu setzen.
Aber nach den jetzigem Wissensstand bin ich mir da nicht mehr sicher ob das nötig wäre


Meine Frage also: reicht CloseAction := caFree; im Close-Event um ein Formular verlässlich zu zerstören und auf nil zu setzen?

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 840
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Mehrere geöffnete Formulare

Beitrag von fliegermichl »

Das Formular weiss doch gar nicht, ob und welcher Variable es zugeordnet wurde. Das ist wie bei allen anderen Variablen auch.
CloseAction := caFree gibt das Formular frei aber setzt keinen Variablenwert.

Code: Alles auswählen

procedure Test;
var p1, p2 : PByte; // Zeiger auf ein Byte
begin
 p1 := GetMem(1); // es wurde ein Byte Speicher reserviert und der Variablen p1 zugewiesen
 p2 := p1; // p2 zeigt jetzt auf das gleiche Byte wie p1
 // Die folgenden beiden Zeilen sind in etwa das, was FreeAndNil macht
 FreeMem(p1, 1); // Den reservierten Speicher wieder freigeben
 p1 := nil; 
end;
Jetzt ist p1 = nil. p2 jedoch nicht. Das zeigt immer noch auf den Bereich wo früher das Byte reserviert war.
Dabei spielt es auch keine Rolle, ob das ein Zeiger auf ein Byte oder auf die Instanz einer Klasse ist.

Das ist auch der Grund warum TComponent diese Notifications hat. Wenn eine UnterKomponente freigegeben wird, dann teilt diese
das allen registrierten Komponenten mit, damit diese ihre Verweise auf die zu löschende Komponente entfernen.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 840
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Mehrere geöffnete Formulare

Beitrag von fliegermichl »

Man kann z.B. auch ein Formular erzeugen und freigeben ohne daß es jemals einer Variablen zugewiesen wird.

Code: Alles auswählen

type
 TMyForm = class ( TForm )
  procedure FormClose(Sender : TObject; var CloseAction : TCloseAction);
 end;
 
procedure TMyForm.FormClose(Sender : TObject; var CloseAction : TCloseAction);
begin
 CloseAction := caFree;
end;

begin
 with TMyForm.Create(nil) do
 begin
  CloseAction := @FormClose;
  Show;
 end;
end.


Hier wird ein Formular erzeugt und angezeigt und wenn man auf das X klickt auch wieder geschlossen und freigegeben aber es wurde niemals einer Variablen zugeordnet die man auf nil setzen könnte.

wp_xyz
Beiträge: 3632
Registriert: Fr 8. Apr 2011, 09:01

Re: Mehrere geöffnete Formulare

Beitrag von wp_xyz »

Bernie110 hat geschrieben:
Do 18. Feb 2021, 15:16
@Frm_PERSONAL_STAMM_UEBERSICHTClose Das man die Procedure so aufrufen kann, konnte ich mir beim besten Willen nicht vorstellen....
Da muss ich "oberlehrerhaft" nochmals dazwischenfunken: Die Prozedur wird hier auch gar nicht "aufgerufen". Sie wird lediglich dem OnClose-Event zugewiesen. Damit "weiß" die Klasse (in diesem Fall TFrm_PERSONAL_STAMM_UEBERSICHT), was im Fall des OnClose-Events zu tun ist.

Ungewöhnlich ist für dich vielleicht das @ am Anfang. Das ist der Adressoperator. Denn Event-Behandlungsroutinen sind letzendlich Prozedur-Variablen, also Pointer, die auf die Adresse der Prozedur zeigen, die im Fall des Ereignisses auszuführen ist.

Das @ muss übrigens entfallen, wenn die Unit im Delphi-Modus arbeitet, also {$mode Delphi} am Kopf der Unit steht statt {$mode objfpc}.

charlytango
Beiträge: 333
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Mehrere geöffnete Formulare

Beitrag von charlytango »

fliegermichl hat geschrieben:
Fr 19. Feb 2021, 10:52
Man kann z.B. auch ein Formular erzeugen und freigeben ohne daß es jemals einer Variablen zugewiesen wird.

Code: Alles auswählen


begin
 with TMyForm.Create(nil) do
 begin
  CloseAction := @FormClose;
  Show;
 end;
end.
Wieder etwas dazu gelernt. Ob das außer einem modalen Fenster klug ist sei dahingestellt.
Und ohne die "with" Konstruktion könnte man gar nicht auf das Formular zugreifen nehm ich mal an.

egal.... es geht an meiner Frage vorbei:

Meine Frage: reicht CloseAction := caFree; im Close-Event um ein Formular verlässlich zu zerstören und auf nil zu setzen?

Sieben
Beiträge: 106
Registriert: Mo 24. Aug 2020, 14:16
OS, Lazarus, FPC: Ubuntu Xenial 32, Lazarus 2.0.10, FPC 3.2.0
CPU-Target: i386

Re: Mehrere geöffnete Formulare

Beitrag von Sieben »

Nein, weil du wie gesagt kein 'Formular auf nil setzen' kannst, sondern nur eine Formular- bzw Klassenvariable. Und die Klasse / das Formular hat 'von selbst' keine Kenntnis dieser Variablen, sie wird ihr immer nur 'von aussen' zugewiesen, üblicherweise durch Create:

Code: Alles auswählen

MyForm := TMyForm.Create;
Da du statt 'MyForm' aber auch 'ThisForm' oder 'ThatForm' nehmen kannst - wie soll das arme Ding das wissen...?

Benutzeravatar
Winni
Beiträge: 665
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.0.12, fpc 3.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Mehrere geöffnete Formulare

Beitrag von Winni »

Hallo Bernie110!

Bevor das Hätte-Wäre-Wenn hier weitergeht und Du Dich mit den Tipps der Kollegen im Kreis drehst, hab ich Dir mal ein ganz simples Beispiel mit 5 Forms gemacht.

Der Zustand der Forms wird angezeigt und LinksKlick zeigt die Form an bzw. bringt sie nach vorne. RechtsKlick schliesst die Form.

Du darfst die Formen auch mit dem System-Menü schließen - der korrekte Status wird angezeigt.

Ich würde sagen: Nimm das mal als Basis und füge den einzelnen Formen Deine gewünschte Funktionalität hinzu.

Dazu müsstest Du Dich von Deinem bisherigen Code verabschieden - das fällt schwer,; ich weiß. Aber so hangelst Du Dich von einem Problem zum nächsten, weil das Design falsch ist.

Winni
Dateianhänge
ShowForms.zip
(130.54 KiB) 17-mal heruntergeladen
ShowForms.png
ShowForms.png (114.03 KiB) 322 mal betrachtet

charlytango
Beiträge: 333
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Mehrere geöffnete Formulare

Beitrag von charlytango »

Winni hat geschrieben:
Fr 19. Feb 2021, 14:24
Bevor das Hätte-Wäre-Wenn hier weitergeht und Du Dich mit den Tipps der Kollegen im Kreis drehst, hab ich Dir mal ein ganz simples Beispiel mit 5 Forms gemacht.
Chapeau... bin beeindruckt

Antworten