Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Projektplanung und -Management (https://www.delphipraxis.net/85-projektplanung-und-management/)
-   -   (Formular-)Handling größerer Projekte (https://www.delphipraxis.net/184235-formular-handling-groesserer-projekte.html)

mm1256 11. Mär 2015 10:42

(Formular-)Handling größerer Projekte
 
Hallo DP-Gemeinde,

wenn eine App nur ein oder wenige Fenster hat, ist die Sache ja ziemlich einfach. Da kann man mit der Delphi-Automatik (Erzeugen der Formulare in der dpr) gut leben. Etwas anders sieht es aus, wenn die App größer wird. Hier gibt es wohl sehr unterschiedliche Ansichten, Meinungen und Praktiken. Das bringt mich zu der Frage, ob denn meine Praxis was taugt und ob man vielleicht daran noch etwas optimieren könnte?

Hintergrund meiner Frage ist auch: Ich bin gelerter Elektriker der sich das Programmieren im Selbststudium angeeignet hat. Ich hab also noch keine Uni von innen gesehen. Manchmal stelle ich mir darum die Frage, ob das was ich mache auch Stand der Technik ist. Weil, für Optimierungen muss man(n) bekanntlich immer ein offenes Ohr haben.

Generell erzeuge ich in der dpr nur die Datenmodule und das Hauptformular:

Delphi-Quellcode:
{------------------------------------------------------------------------------}
{-} {$I Project1.inc} {--------------------------------------------------------}
{------------------------------------------------------------------------------}

program Project1;

uses
  {$IFDEF DEBUG}
  FastMM4,
  {$ENDIF}
  Vcl.Forms,
  uMainForm in 'uMainForm.pas' {FrmMainForm},
  uClientForm1 in 'uClientForm1.pas' {FrmClient1},
  uClientForm2 in 'uClientForm2.pas' {FrmClient2},
  uMainData in 'uMainData.pas' {MainData: TDataModule};

{$R *.res}

begin
  {$IFDEF DEBUG}
  ReportMemoryLeaksOnShutdown := True;
  {$ENDIF}
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TMainData, MainData);
  Application.CreateForm(TFrmMainForm, FrmMainForm);
  Application.Run;
end.
Die INC sieht so aus:

Delphi-Quellcode:
{------ Standard-Compiler-Optionen --------------------------------------------}
{-----} {$A+,B+,C+,E-,F-,G+,H+,J+,K-,M-,N+,O+,P+,S-,T-,U-,V+,W-,X+,Y-,Z1} {----}
{$IFDEF DEBUG}    {$DEFINE TEST}                                        {$ENDIF}
{- Range-Check und Debug-Info wahlweise beim Testen ein/aus -------------------}
{$IFDEF TEST}     {$DEFINE UseRangeCheck} {$DEFINE CreateDebugInfo}     {$ENDIF}

{$IFDEF UseRangeCheck}
  {$Q+,R+}
{$ELSE}
  {$Q-,R-}
{$ENDIF UseRangeCheck}

{$IFDEF CreateDebugInfo}
  {$D+ L+}
  {$OPTIMIZATION  ON}
  {$DEBUGINFO     ON}
  {$LOCALSYMBOLS  ON}
  {$REFERENCEINFO ON}
  {$DEFINITIONINFO ON}
  {$ASSERTIONS    ON}
{$ELSE}
  {$OPTIMIZATION  ON}
  {$DEBUGINFO     OFF}
  {$LOCALSYMBOLS  OFF}
  {$REFERENCEINFO OFF}
  {$DEFINITIONINFO OFF}
  {$ASSERTIONS    OFF}
{$ENDIF CreateDebugInfo}
Das Hauptformular:

Delphi-Quellcode:
{------------------------------------------------------------------------------}
{-} {$I Project1.inc} {--------------------------------------------------------}
{------------------------------------------------------------------------------}

unit uMainForm;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TFrmMainForm = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  FrmMainForm: TFrmMainForm;

implementation

{$R *.dfm}

uses uClientForm1, uClientForm2;

procedure TFrmMainForm.Button1Click(Sender: TObject);
begin
  FrmClient1_Show;
end;

procedure TFrmMainForm.Button2Click(Sender: TObject);
begin
  case FrmClient2_ShowModal of
    mrOk    : ShowMessage('OK');
    mrCancel : ShowMessage('Cancel');
  end;
end;

end.
Normales Formular (ist bei etwa 8 Formularen ein MDI-Formular)

Delphi-Quellcode:
{------------------------------------------------------------------------------}
{-} {$I Project1.inc} {--------------------------------------------------------}
{------------------------------------------------------------------------------}

unit uClientForm1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TFrmClient1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  FrmClient1: TFrmClient1 = nil;
  FrmClient1Active: boolean = false;

procedure FrmClient1_Show;

implementation

{$R *.dfm}

procedure FrmClient1_Show;
begin
  if not FrmClient1Active
  then FrmClient1 := TFrmClient1.Create(Application);
  FrmClient1.Show;
end;

procedure TFrmClient1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  FrmClient1Active := false;
end;

procedure TFrmClient1.FormCreate(Sender: TObject);
begin
  FrmClient1Active := true;
end;

end.
Modale Formulare:

Delphi-Quellcode:
{------------------------------------------------------------------------------}
{-} {$I Project1.inc} {--------------------------------------------------------}
{------------------------------------------------------------------------------}

unit uClientForm2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TFrmClient2 = class(TForm)
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

function FrmClient2_ShowModal: integer;

implementation

{$R *.dfm}

function FrmClient2_ShowModal: integer;
begin
  with TFrmClient2.Create(Application) do
  try
    Result := ShowModal;
  finally
    Release;
  end;
end;

end.
Detail-Fragen:

1) Gibt es hinsichtlich einer optimalen Exe-Größe bei der Include-Datei noch Optimierungsmöglichkeiten?

2) Bei den normalen und MDI-Fenstern würde ich gerne die globalen Variablen lokal machen, also im implemantation-Teil, damit der versehentliche Zugriff aus dem Hauptformular bzw. auch anderen Formularen (wenn ein untergeordnetes Formular wiederum ein weiteres Formular aufruft) ausgeschlossen ist. Ich habe es testweise mal probiert und keine Nebenwirkungen festgestellt.

oder

3) Macht es Sinn, die Boolean-Variablen weg zu lassen, und stattdessen auf NIL zu prüfen? Wenn ja, wie geht das zuverlässig? Stichwort mögliche Exceptions beim Anzeigen oder Schließen.

Ich möchte in jedem Fall wie bisher den Speicher beim Beenden eines Formulares wieder frei geben denn es befinden sich insgesamt über 100 Formulare im Projekt.

Vielen Dank schon mal für eure Anregungen.

Nersgatt 11. Mär 2015 10:51

AW: (Formular-)Handling größerer Projekte
 
Zu 1) weiß ich nicht. :stupid:
zu 2) Man sollte Variablen immer so lokal wie möglich deklarieren. Von daher ist das eine gute Idee
zu 3) Du kannst mit Assigned(FrmClient1) prüfen, ob die Variable zugewiesen ist. Dabei solltest Du aber beim Freigeben des Formulars FreeAndNil(FrmClient1) nutzen. Dann funktioniert das zuverlässig und Du kannst die Boolschen Variablen einsparen. Der Fachbegriff für das, was Du da machst, ist übrigens "lazy initialization"

Die Vorgehensweise beim Modalen Formular kannst Du so machen. Ich würde es etwas anders schreiben (lokale Variable für die Instanz deklarieren und das with weglassen), das ist hier im Endeffekt egal.

mm1256 11. Mär 2015 11:00

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von Nersgatt (Beitrag 1293008)
zu 3) Du kannst mit Assigned(FrmClient1) prüfen, ob die Variable zugewiesen ist. Dabei solltest Du aber beim Freigeben des Formulars FreeAndNil(FrmClient1) nutzen.....

Mit Assigned(FrmClient1) und FreeAndNil(FrmClient1) habe ich ja ursprünglich beim Erstellen der Formulare (ist schon 10+ Jahre her) rum gespielt und es hat immer wieder geknallt. Die Frage ist, an welcher Stelle (welchem Formular-Event) soll man "FreeAndNil" denn verwenden, damit es bei normalen und MDI-Fenstern gleichermaßen funzt.

bcvs 11. Mär 2015 11:30

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von mm1256 (Beitrag 1293012)
Die Frage ist, an welcher Stelle (welchem Formular-Event) soll man "FreeAndNil" denn verwenden, damit es bei normalen und MDI-Fenstern gleichermaßen funzt.

Ich mache im Prinzip sowas:
Delphi-Quellcode:
procedure TFrmClient1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  FrmClient1:=nil;
end;
Also kein FreeAndNil, denn das Free wird ja automatisch durch das Action:=caFree ausgelöst.

Delbor 11. Mär 2015 11:41

AW: (Formular-)Handling größerer Projekte
 
Hi mm1256

Wenn du das Formular aus sich selber heraus schliessen willst, dann musst du es mit Release freigeben. Machst du das mit Free oder FreeAndNil, wird das Formular freigegeben, und die Ausführung kehrt zur auslösenden Prozedur zurück - die nicht mehr da ist, da das Formular freigegeben wurde. Und desshalb knallts.
Wenn du hingegen von einem MDI-Fenster ein MDI-Childd freigibst (zB.), passiert das nicht.

Gruss
Delbor

mm1256 11. Mär 2015 11:45

AW: (Formular-)Handling größerer Projekte
 
Danke für den Tipp. Hab es soeben mal ausprobiert. Funktioniert.

Ein Verständnisproblem (ich möchte ja auch verstehen was ich mache und nicht nur irgend einen Code abschreiben :-D) hab ich noch: Wenn ich im "OnClose" mit FrmClient1:=nil die Variable FrmClient1 auf NIL setze, wie schafft es Delphi (der Speichermanager oder was auch immer...) dann hinterher die Variable FrmClient1 trotzdem noch frei zu geben?

mm1256 11. Mär 2015 11:49

AW: (Formular-)Handling größerer Projekte
 
@Delbor Sorry, hat sich jetzt überschnitten.

Die Verwendung von Release bei modalen Fenstern ist mir klar. Aber bei normalen und MDI-Fenstern verstehe ich das noch nicht ganz. So wie es aussieht scheint aber die von "bcvs" vorgeschlagene Variante zu funktionieren.

bcvs 11. Mär 2015 11:56

AW: (Formular-)Handling größerer Projekte
 
Du erzeugst ja eine Instanz der Klasse TForm. Diese Diese Instanz gibt sich selbst frei, wenn im das Action im Onclose auf caFree steht. Dazu braucht es eigentlich überhapt keine Instanzvariable (in deinem Fall FrmClient1).

Dein FrmClient zeigt nur zusätzlich noch auf die Instanz, kann aber auch nil sein oder theoretisch sonst irgendwohin zeigen.

mm1256 11. Mär 2015 12:08

AW: (Formular-)Handling größerer Projekte
 
Jetzt ist mir das auch klar. "zusätzlich" war das magische Wort. Vielen Dank nochmals für deine Hilfe!

Delbor 12. Mär 2015 09:41

AW: (Formular-)Handling größerer Projekte
 
Hi zusammen

Zitat:

Zitat von bcvs (Beitrag 1293019)
Diese Diese Instanz gibt sich selbst frei, wenn im das Action im Onclose auf caFree steht.

Das stimmt so nicht. Der Parameter Action steuert lediglich, was geschehen soll. Wird ihm caFree zugewiesen, wird das Formular tatsächlich geschlossen
Nach wie vor: ein Formular, das sich selbst freigeben soll, muss mit Release geschlossen werden.
Ein Beispiel:
Delphi-Quellcode:
var TForm1;

implementation;

...
...

procedure TForm1.ButtonClose.Onclick(Sender:TObject);
begin
Form1.Close;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:= caFree;
//Action := caHide;
end;
Hier klickt der Anwender auf den Button, der sich in Form1 befindet. Dadurch wird OnClose aufgerufen; die Programmausführung arbeitet also FormOnClose ab und tut das, was der Parameter Action vorsieht. In diesem Fall wird das Formular freigegeben (es könnte auch nur versteckt (etc) werden).
Nachdem FormClose abgearbeitet ist, kehrt die Programmmausführung zur aufrufenden Prozedur zurück - aber Scheibenkleister, die ist ja gar nicht mehr da!! Also was macht Delphi! Eine AV auslösen?

Mit Form1.Release kann dies verhindert werden.
Hier solltest du dir die Warnung zu Herzen nehmen

Ein modales Formular kann, muss aber nicht mit Release freigegeben werden, da sich die Behandlungsroutine nicht im modalen Formular befindet.

Gruss
Delbor

himitsu 12. Mär 2015 09:53

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von Delbor (Beitrag 1293128)
Ein modales Formular kann, muss aber nicht mit Release freigegeben werden, da sich die Behandlungsroutine nicht im modalen Formular befindet.

Dennoch Release oder besser Close statt einem Free, denn auch dieser Code greift weiterhin auf die Form zu.

Sir Rufo 12. Mär 2015 10:20

AW: (Formular-)Handling größerer Projekte
 
Eigentlich kann man sagen, dass bei größeren Projekten die globalen Form-Variablen quasi als non existent zu betrachten sind. Die gibt es einfach nicht mehr.

Sobald von einer Form mehrere Instanzen benötigt werden, hat man eh ein Problem, denn die globale kann ja nur eine Referenz speichern. Also merkt sich der Erzeuger der Form diese Referenz und gibt diese je nach Bedarf wieder frei.

mm1256 12. Mär 2015 10:26

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von Sir Rufo (Beitrag 1293133)
Sobald von einer Form mehrere Instanzen benötigt werden, hat man eh ein Problem, denn die globale kann ja nur eine Referenz speichern.....

Das Problem mit mehreren Instanzen "des selben Formulars" habe ich ja Gott sei Dank nicht. Die MDI's erben von einem gemeinsamen Vorfahren alle grundlegenden Eigenschaften und die anderen "normalen" Formulare sind eindeutig, d.h. es wird immer nur eine einzige Instanz benötigt.

Der Bahnhof ist für mich jedoch noch nicht kleiner geworden. Wenn ich - respektive der User - einen Button zum Schließen hat, dann ist die Sache schon klar. Aber, ich hab doch ausgenommen von modalen Fenstern i.d.R. keinen Close-Button. Darum die Frage, wenn ich denn schon Release verwenden soll, wo? Und wo setzt man die Form-Variable auf nil? Muss ja gemacht werden, sonst müsste ich ja die bisherige Boolean-Variable weiter verwenden.

So langsam kommt mir der Verdacht, dass die aus der Not geborene Variante (ich wusste bzw. weiß es ja nicht besser, und sogar die Experten sind sich nicht offensichtlich ganz so einig wie man es besser oder idealerweise macht) mit der Hilfsvariablen gar nicht so schlecht ist.

Darum nochmals die Frage: Ist das jetzt der Königsweg? Oder was funktioniert in dieser Variante nicht so wie gewollt? Speicherleck scheint es ja keines zu geben, denn mit FastMM und aktiviertem "ReportMemoryLeaksOnShutdown := True" kommt jedenfalls keine Meldung.

Delphi-Quellcode:
{-Interface-procedure zum Anzeigen der Form-}
procedure FrmClient1_Show;
begin
  if not Assigned(FrmClient1)
  then FrmClient1 := TFrmClient1.Create(Application);
  FrmClient1.Show;
end;

procedure TFrmClient1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  FrmClient1 := nil;
end;

procedure TFrmClient1.FormCreate(Sender: TObject);
begin // EDIT: Sorry, das braucht man ja nicht mehr
  FrmClient1Active := true;
end;

bcvs 12. Mär 2015 10:59

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von Delbor (Beitrag 1293128)
Zitat:

Zitat von bcvs (Beitrag 1293019)
Diese Diese Instanz gibt sich selbst frei, wenn im das Action im Onclose auf caFree steht.

Das stimmt so nicht. Der Parameter Action steuert lediglich, was geschehen soll. Wird ihm caFree zugewiesen, wird das Formular tatsächlich geschlossen
Nach wie vor: ein Formular, das sich selbst freigeben soll, muss mit Release geschlossen werden.

Doch, das stimmt. Hast du dein Beispiel mal ausprobiert? Das funktioniert einwandfrei, ohne AV.

Mit Action:= caFree wird das Formular geschlossen und der Speicher freigegeben, wie es auch in der von dir zitierten Hilfe steht. Dort steht allerdings nicht, wann und wie diese Freigabe geschieht. Ein Blick in den Quelltext von TCustomForm zeigt, dass dann nach dem Aufruf von OnClose einfach ein Release gemacht wird. Also sind wir uns wieder einig :cheers:

bcvs 12. Mär 2015 11:23

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von mm1256 (Beitrag 1293136)
Aber, ich hab doch ausgenommen von modalen Fenstern i.d.R. keinen Close-Button. Darum die Frage, wenn ich denn schon Release verwenden soll, wo? Und wo setzt man die Form-Variable auf nil?

Genau für diesen Fall gibt es ja das OnClose-Ereignis, damit du da nochmal eingreifen kannst. Und ob du Release selbst aufrufst oder nicht ist Geschmackssache.
Das hier:
Delphi-Quellcode:
procedure TFrmClient1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  FrmClient1 := nil;
end;
bewirkt genau das selbe wie:
Delphi-Quellcode:
procedure TFrmClient1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caNone;
  FrmClient1 := nil;
  Release;
end;
Ich habe hier übrigens auch wie du Forms, von denen es nur eine Instanz geben kann. Dabei prüfe ich deren Existenz auch über die Form-Variablen. Bei allen anderen Forms (modale Dialoge etc) lösche ich die von Delphi automatisch erzeugte Form-Variable immer als erstes raus.

mm1256 12. Mär 2015 11:23

AW: (Formular-)Handling größerer Projekte
 
Chapeau! Bei modalen Fenstern :thumb:

Delphi-Quellcode:
procedure TCustomForm.CloseModal;
var
  CloseAction: TCloseAction;
begin
  ...
    case CloseAction of
      caNone: ModalResult := 0;
      caFree: Release;
    end;
  ...
end;
Aber bei "normalen" Fenstern ???

Delphi-Quellcode:
procedure TCustomForm.DoClose(var Action: TCloseAction);
begin
  if Assigned(FOnClose) then FOnClose(Self, Action);
end;
EDIT:

Jetzt hab ich's kapiert, und darum werde ich es zukünftig so machen:

Delphi-Quellcode:
procedure TFrmClient1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caNone; // In der CustomForm soll nichts passieren
  FrmClient1 := nil; // Meine Form-Variable ist jetzt ungültig
  Release;          // Ich gebe das Formular selber frei
end;
Wenn da mal jemand über meinen Code stolpern sollte, sieht man gleich, was passiert.

Vielen Dank für deine Hilfe und Geduld!

BadenPower 12. Mär 2015 12:24

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von mm1256 (Beitrag 1293171)
Delphi-Quellcode:
procedure TFrmClient1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caNone; // In der CustomForm soll nichts passieren
  FrmClient1 := nil; // Meine Form-Variable ist jetzt ungültig
  Release;          // Ich gebe das Formular selber frei
end;

Genau das gleiche passiert, wenn Du es nur so machst:

Delphi-Quellcode:
procedure TFrmClient1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;
Warum denn noch zwei Schritte machen, welche automatisch gemacht werden?

Hat auch den wichtigen Vorteil, dass dies dann auch bei jeder Instanz Deiner Form funktioniert und nicht nur wenn der Zeiger der Instanz auf die globale Variable "FrmClient1" zeigt.

mm1256 12. Mär 2015 12:41

AW: (Formular-)Handling größerer Projekte
 
Die modalen Fenster sind ja abgehakt. Es geht ja um nicht modale Fenster. Das ist ja mein aktueller Ansatz (siehe Eröffnung des Thread) aber dann kannst du eben nicht mehr auf "Formvariable = nil" bei der Erstellung prüfen, und brauchst die Hilfsvariable.

Oder du erzeugst mit jedem Aufruf ein neues Formular, anstatt das bestehende anzuzeigen. Wenn du das willst, dann brauchst du ja gar keine Formularvariable:

Delphi-Quellcode:
  With TFrmClient1.Create(Application) do Show;

rocedure TFrmClient1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

BadenPower 12. Mär 2015 13:01

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von mm1256 (Beitrag 1293192)
Oder du erzeugst mit jedem Aufruf ein neues Formular, anstatt das bestehende anzuzeigen. Wenn du das willst, dann brauchst du ja gar keine Formularvariable:


Dazu überprüfe ich das Anwendungsobjekt, ob bereits irgendeine Instanz des Fensters erzeugt wurde:

Delphi-Quellcode:
function ShowFormOnce(AFormClass: TComponentClass): TForm;
var
  liZ1: Integer;
begin
  Result := nil;
  for liZ1 := 0 to Application.ComponentCount-1 do
   begin
    if (Application.Components[liZ1] is AFormClass) then
     begin
      Result := TForm(Application.Components[liZ1]);
     end;
   end;

  if (Result = nil) then
   begin
    Result := TForm(AFormClass.Create(Application));
   end;

  Result.Show;

end;
wobei "AFormClass" die Klasse des entsprechenden Fensters ist.

Existiert eine Instanz, dann wird diese angezeigt anderenfalls wird eine Instanz erzeugt und danach angezeigt.

mm1256 12. Mär 2015 13:31

AW: (Formular-)Handling größerer Projekte
 
Interessanter Ansatz. Werde ich mal nachverfolgen. Zu deinem Code-Beispiel...da fehlt natürlich noch eine Kleinigkeit:

Delphi-Quellcode:
function ShowFormOnce(AFormClass: TComponentClass): TForm;
var
  liZ1: Integer;
begin
  Result := nil;
  for liZ1 := 0 to Application.ComponentCount-1 do
   begin
    if (Application.Components[liZ1] is AFormClass) then
     begin
      Result := TForm(Application.Components[liZ1]);
     end;
   end;

  if (Result <> nil) then
   begin
    Result := TForm(AFormClass.Create(Application));
   end;
  if Assigned(Result) then // sonst knallts !!!!
  Result.Show;

end;
EDIT: Mal "nachverfolgt". Würde dann so aussehen:

Delphi-Quellcode:
{-Interface-procedure zum Anzeigen der Form-}
procedure FrmClient1_Show;
var
  i: Integer;
begin
  for i := 0 to Pred(Application.ComponentCount)
  do if (Application.Components[i] is TFrmClient1) then
  begin
    TFrmClient1(Application.Components[i]).Show;
    Exit;
  end;
  with TFrmClient1.Create(Application) do Show;
end;

procedure TFrmClient1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;
Einwände? - Verbesserungsvorschläge?

Delbor 12. Mär 2015 13:52

AW: (Formular-)Handling größerer Projekte
 
Hi zusammen
Zitat:

Zitat von bcvs (Beitrag 1293159)
Zitat:

Zitat von Delbor (Beitrag 1293128)
Zitat:

Zitat von bcvs (Beitrag 1293019)
Diese Diese Instanz gibt sich selbst frei, wenn im das Action im Onclose auf caFree steht.

Das stimmt so nicht. Der Parameter Action steuert lediglich, was geschehen soll. Wird ihm caFree zugewiesen, wird das Formular tatsächlich geschlossen
Nach wie vor: ein Formular, das sich selbst freigeben soll, muss mit Release geschlossen werden.

Doch, das stimmt. Hast du dein Beispiel mal ausprobiert? Das funktioniert einwandfrei, ohne AV.

Mit Action:= caFree wird das Formular geschlossen und der Speicher freigegeben, wie es auch in der von dir zitierten Hilfe steht. Dort steht allerdings nicht, wann und wie diese Freigabe geschieht. Ein Blick in den Quelltext von TCustomForm zeigt, dass dann nach dem Aufruf von OnClose einfach ein Release gemacht wird. Also sind wir uns wieder einig :cheers:

Ich hab mir nun mal ein kleines Beispielprogramm geschrieben:
Delphi-Quellcode:
unit TestMainUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Menus;

type
  TForm1 = class(TForm)
    btn1: TButton;
    MainMenu1: TMainMenu;
    Datei1: TMenuItem;
    Bearbeiten1: TMenuItem;
    procedure btn1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btn1Click(Sender: TObject);
begin
     Form1.Close;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
     Action := caFree;
end;

end.
Tatsächlich funktioniert das. Und zwar deshalb, weil in TCustomForm.Close geprüft wird, ob die Form die Mainform ist und anschliessend Application.Terminate aufgerufen wird (DelphiXE4). Nach einigen elses wird auch Release aufgerufen. Beim Durchsteppen sprang meine 'ausgeführte Zeile' gleich nach dem Terminate auf end.
Das verblüfft mich doch ziemlich, da auf Delphi-Treff lange ein Tutorial zum schliessen eines Objektes (einer Form?) aus sich selbst heraus zu finden war.
Kann es sein, dass dieses Release erst in neueren Delphiversionen integriert wurde?

Gruss
Delbor

BadenPower 12. Mär 2015 13:54

AW: (Formular-)Handling größerer Projekte
 
[QUOTE=mm1256;1293209]
Delphi-Quellcode:
  if (Result = nil) then
   begin
    Result := TForm(AFormClass.Create(Application));
   end;
  if Assigned(Result) then // sonst knallts !!!!
  Result.Show;
Wenn Du hier Angst hast, dass es knallt und deshalb die Assigned-Abfrage eingefügt hast, dann darfst Du das unten auch nicht so machen:

Delphi-Quellcode:
  with TFrmClient1.Create(Application) do Show;
.
EDIT:
Da war ein Fehler in der Abfrage

mm1256 12. Mär 2015 14:01

AW: (Formular-)Handling größerer Projekte
 
Dazu kommt es doch gar nicht, denn denn wäre ja
Code:
Application.Components[i] is TFrmClient1
schon FALSE

Delbor 12. Mär 2015 14:09

AW: (Formular-)Handling größerer Projekte
 
Hi mm1256

Alternativ könntest du die Clientforms, die du zur Laufzeit erstellst, auch in eine (generische?) Objektliste stecken, dann müsstest du nicht alle Komponenten der Anwendung durchlaufen, sondern nur den Listeninhalt. Solltest du eine ältere Delphiversion haben, die noch keine Objectlisten kennt, kannst du auch ein TList-Objekt benutzen und da einen Zeiger ablegen.

Gruss
Delbor

Sir Rufo 12. Mär 2015 14:15

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von Delbor (Beitrag 1293220)
Hi mm1256

Alternativ könntest du die Clientforms, die du zur Laufzeit erstellst, auch in eine (generische?) Objektliste stecken, dann müsstest du nicht alle Komponenten der Anwendung durchlaufen, sondern nur den Listeninhalt. Solltest du eine ältere Delphiversion haben, die noch keine Objectlisten kennt, kannst du auch ein TList-Objekt benutzen und da einen Zeiger ablegen.

Gruss
Delbor

Dafür nimmt man Delphi-Referenz durchsuchenSystem.Contnrs.TComponentList, denn dafür wurde die gebaut

mm1256 12. Mär 2015 14:15

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von Delbor (Beitrag 1293220)
Hi mm1256

Alternativ könntest du die Clientforms, die du zur Laufzeit erstellst, auch in eine (generische?) Objektliste stecken, dann müsstest du nicht alle Komponenten der Anwendung durchlaufen.....

Danke für den Tipp. Application.ComponentCount hab ich natürlich vorher mal getestet, weil, Performance möchte ich nach Möglichkeit nicht verschenken. Auch wenn's nur Kleinigkeiten sind. Es scheint so zu sein, dass Application.ComponentCount nur die Anzahl der aktiven Fenster beinhaltet. Weil ich diese ja immer wieder freigebe, wenn sie nicht mehr gebraucht werden, dürfte Application.ComponentCount i.d.R. nicht viel größer als 10 sein. Ich denke, das ist also keine Spassbremse :thumb:

EDIT

Zitat:

Zitat von Sir Rufo (Beitrag 1293221)
Dafür nimmt man Delphi-Referenz durchsuchenSystem.Contnrs.TComponentList, denn dafür wurde die gebaut

Geil, was ihr so alles wisst. Das kannte ich noch gar nicht.

Sir Rufo 12. Mär 2015 14:20

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von mm1256 (Beitrag 1293222)
Es scheint so zu sein, dass Application.ComponentCount nur die Anzahl der aktiven Fenster beinhaltet.

Wenn du
Delphi-Quellcode:
Application
als Owner nur den Formularen gibst, was soll da auch sonst noch drin sein?

Dort ist das drin, was du da rein steckst:
Delphi-Quellcode:
program foo;

...

begin
  Application.Initialize;
  Application.CreateForm( TFooForm, FooForm ); // <- Da, wird Owner
  Application.Run;
end.

BadenPower 12. Mär 2015 14:27

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von Delbor (Beitrag 1293220)
dann müsstest du nicht alle Komponenten der Anwendung durchlaufen,

Über wieviele Millisekunden reden wir hier?

Du gehst ja nicht alle Komponenten der Anwendung durch, sondern wie Sir Rufo schon richtig angemerkt hat, nur die Komponenten deren Owner "Application" ist.
Und dies sind eben meistens nur die Formulare.

mm1256 12. Mär 2015 14:28

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von Sir Rufo (Beitrag 1293223)
Wenn du
Delphi-Quellcode:
Application
als Owner nur den Formularen gibst, was soll da auch sonst noch drin sein?

Ooooops....stimmt. Eigentlich logisch. Danke für die Erklärung. :thumb:

Delbor 12. Mär 2015 16:42

AW: (Formular-)Handling größerer Projekte
 
Hi zusammen
Zitat:

Zitat von BadenPower (Beitrag 1293225)
Zitat:

Zitat von Delbor (Beitrag 1293220)
dann müsstest du nicht alle Komponenten der Anwendung durchlaufen,

Über wieviele Millisekunden reden wir hier?

Du gehst ja nicht alle Komponenten der Anwendung durch, sondern wie Sir Rufo schon richtig angemerkt hat, nur die Komponenten deren Owner "Application" ist.
Und dies sind eben meistens nur die Formulare.

Daran hab ich erstmal nicht gedacht - mein wirklich 'rotes Tuch' ist denn auch zum Bleistift Findcomponent, das die kompletten Komponenten eines Containers durchläuft. Wenn dieser eine Form ist, kann das recht happig werden, wenn jede Komponente so identifiziert werden soll...


Gruss
Delbor

bcvs 12. Mär 2015 17:15

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von Delbor (Beitrag 1293212)
Kann es sein, dass dieses Release erst in neueren Delphiversionen integriert wurde?

Nein, das war schon immer so.

mm1256 12. Mär 2015 17:27

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von Delbor (Beitrag 1293261)
Daran hab ich erstmal nicht gedacht - mein wirklich 'rotes Tuch' ist denn auch zum Bleistift Findcomponent, das die kompletten Komponenten eines Containers durchläuft.....

Genauso oder zumindest so ähnlich hab ich gedacht, als ich das Beispiel von BadenPower zum ersten mal gelesen habe. Daraufhin meine größte dpr mit den schon zitierten 100+ Formularen aufgemacht, ein paar Fenster geöffnet und dann mal gecheckt, wie hoch Application.ComponentCount ist. Nach SirRufo's Kommentar ist mir dann auch schnell klar geworden, warum das so niedrig ist. Wenn man in der dpr nur ein einziges Formular (das Hauptformular) erzeugt, dann ist das wirklich eine prima Sache, und das von dir zitierte "rote Tuch" ist dann flugs samt den Vorurteilen verschwunden.

Was mich abschließend mal zu dem Kommentar verleitet: Man findet im WEB tausende von Tutorials über wie und was und überhaupt...aber wenn es mal um den Feinschliff größerer Projekte geht, wird es ganz schnell eng. Nur gut, dass es die DP gibt :thumb:

Hansa 12. Mär 2015 20:30

AW: (Formular-)Handling größerer Projekte
 
Bei grösseren Projekten machst Du folgendes: du baust ein Form mit vorgefertigtem caFree im OnClose. Dieses schiebst du in die Objektablage. Ab dann nur noch dieses Formulur vererben,d.h. das caFree ist standardmässig schon da und fertig. Release, free etc. wird dann nicht benötigt. Zumindest nicht bei meinem Programm mit ca, 200 Forms.

BadenPower 12. Mär 2015 21:10

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von mm1256 (Beitrag 1293270)
Man findet im WEB tausende von Tutorials über wie und was und überhaupt...aber wenn es mal um den Feinschliff größerer Projekte geht, wird es ganz schnell eng. Nur gut, dass es die DP gibt :thumb:

Als ich mir dieses Grundgerüst herausgearbeitet habe, da gab es noch gar kein Internet.

Ich glaube dies war beim Erscheinen von Delphi4. Ob ich es schon bei D2 oder gar D1 eingesetzt habe glaube ich aber eher nicht.

Früher war man noch mehr oder weniger wirklich gezwungen sich die Handbücher (7 Stück bei D4) durchzulessen, in welchen doch tatsächlich alle Funktionen beschrieben waren. Das Problem heutzutage ist, dass die meisten User die beigefügte Hilfe nicht von vorne bis hinten durchlesen und daher viele Dinge überhaupt nicht kennen können.

mm1256 13. Mär 2015 06:35

AW: (Formular-)Handling größerer Projekte
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Hansa (Beitrag 1293288)
Bei grösseren Projekten machst Du folgendes: du baust ein Form mit vorgefertigtem caFree im OnClose.....

Das ist ja eigentlich bei größeren Projekten eine Selbstverständlichkeit. ABER: "ein Form" reicht in der Praxis nicht. Ich hab 14 MDI-Fenster in 6 Programmodulen die alle ihre grundlegenden Eigenschaften von einem Vorfahren erben. Das Selbe bei den etwa 40 modalen Dialogen, die eine Interaktion des Users erwarten, den etwa 10 Dialogen welche den Ausdruck steuern (Druck-Voreinstellungen), den etwa 50 modalen Fenstern die programmspezifische Funktionen ausführen (es geht um ein WWS).....und so weiter.

Ich hab soeben mal die Fenster zusammengezählt: 14 MDI, 27 Hauptformulare, 86 modale Dialoge, 121 Formulare mit Hilfsroutinen und dergleichen (Dateisuche, Dateien zippen, Fortschrittanzeigen, Fileviewer, Datenbank-Tools, Rechner, Scanner, Bilderanzeige...). Da sieht man erst, was sich im Lauf der Jahre in einem einzigen Projekt alles ansammelt. Die Vererbung von Formulareigenschaften (welche ja durch die Delphi-IDE wunderbar unterstützt wird) macht man sich da zwangsweise zu Nutzen. Anders geht es ja auch gar nicht. Es sei denn, man legt Wert darauf, dass das Installationsprogramm nicht mehr auf eine CD passt, nur mehr auf DVD. :cyclops:

Ein Beispiel für die Vererbung aus zwei unterschiedlichen Programmteilen habe ich (falls es jemand interessiert) angehängt. Der Linke ist ein Dialog im Hauptprogramm zur Datensicherung und der Rechte der Startdialog für mein Installationsprogramm (natürlich selber programmiert). Also zwei sehr unterschiedliche Bereiche. Der neue Kunde soll bereits bei der Installation auf die Programmoptik vorbereitet werden und derjenige der ein Update installiert, erkennt auf den ersten Blick um was es geht.

Der praktische Effekt: der Anwender hat in vielen Bereichen eine einheitliche Oberfläche. Der technische Aspekt: Es werden viele Ressourcen eingespart. Das bringt mich aber zurück zu einer der Ausgangsfragen:

Zitat:

Zitat von mm1256 (Beitrag 1293006)
1) Gibt es hinsichtlich einer optimalen Exe-Größe bei der Include-Datei noch Optimierungsmöglichkeiten?

Hierzu hat sich noch niemand geäußert...kann ich daraus ableiten, dass hier nichts mehr zu machen ist? Mir geht es weniger um den Platzbedarf der kompletten Installation sondern um die Größe des Download-Image. Die möchte ich so klein wie möglich halten und da sind die Exe-Größen schon mit ausschlaggebend.

Dalai 13. Mär 2015 13:19

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von mm1256 (Beitrag 1293315)
Zitat:

Zitat von mm1256 (Beitrag 1293006)
1) Gibt es hinsichtlich einer optimalen Exe-Größe bei der Include-Datei noch Optimierungsmöglichkeiten?

Hierzu hat sich noch niemand geäußert...kann ich daraus ableiten, dass hier nichts mehr zu machen ist?

Falls du keine erweiterte RTTI verwendest, kann man mit {$WEAKLINKRTTI ON} noch etwas rausholen (mehr Details z.B. in meinem Thema Nachteile WEAKLINKRTTI?). Bei mir hat das ~10% eingespart (auf ~1,5 MiB Dateigröße).

MfG Dalai

Hansa 13. Mär 2015 14:33

AW: (Formular-)Handling größerer Projekte
 
Zitat:

Zitat von mm1256 (Beitrag 1293315)
Zitat:

Zitat von Hansa (Beitrag 1293288)
Bei grösseren Projekten machst Du folgendes: du baust ein Form mit vorgefertigtem caFree im OnClose.....

Das ist ja eigentlich bei größeren Projekten eine Selbstverständlichkeit. ABER: "ein Form" reicht in der Praxis nicht.

Wer redet von einer einzigen Form ? Ich denke es geht um : "(Formular-)Handling größerer Projekte". Und da ist eben die Formularvererbung immens wichtig. Du wirst ja wohl bei 121 Hilf-Forms nicht 121mal ein OnkeyPress etc. programmieren wollen ? Kann aber auch sein, dass du zu den 99% gehörst, die nicht mal wissen was Objektablage eigentlich heisst. Sogar Marco Cantu wundert sich, wie wenig das benutzt wird. Hat er mir jedenfalls so gesagt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:36 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz