AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Wie erstelle ich dynamische forms richtig ?

Ein Thema von Int3g3r · begonnen am 5. Mär 2019 · letzter Beitrag vom 6. Mär 2019
Antwort Antwort
Seite 1 von 3  1 23      
Int3g3r

Registriert seit: 28. Nov 2018
Ort: Schweiz
118 Beiträge
 
Delphi 10.3 Rio
 
#1

Wie erstelle ich dynamische forms richtig ?

  Alt 5. Mär 2019, 11:24
Guten Tag,

Ich möchte wissen wie man Forms dynamisch richtig und ressourcensparend erstellt.

Ich habe das Gefühl das mein momentaner Ansatz viel zu aufwändig / falsch ist.
Der Ansatz funktioniert, nur habe ich zweifel das dieser ressourcen spart da jedes Form komplett erstellt wird.

Wie ihr im code seht möchte ich jedes dynamisch erstellte Form(child) ansprechen können um jeweilige Parameter definieren zu können.
Dazu müssen die childs innerhalb eines containers (scrollbox) positioniert werden.

Ist dieser Ansatz korrekt ? Was könnte ich verbessern ?

Delphi-Quellcode:
unit Unit2;

interface

uses form_child;


type
   TForm2 = class(TForm)
      RadioGroup1: TRadioGroup;
      BitBtn1: TBitBtn;
      cxTimeEdit1: TcxTimeEdit;
      DateTimePicker1: TDateTimePicker;
      JvTimeEdit1: TJvTimeEdit;
    BitBtn2: TBitBtn;
      Label1: TLabel;
      btnCreateChild: TBitBtn;
      BitBtn3: TBitBtn;
      cxScrollbox: TcxScrollBox;
      cxDBTimeEdit1: TcxDBTimeEdit;
      Edit1: TEdit;
      dxMemData1: TdxMemData;
      dxMemData1time: TTimeField;
      DataSource1: TDataSource;
      dxLayoutControl1Group_Root: TdxLayoutGroup;
      dxLayoutControl1: TdxLayoutControl;
      dxLayoutItem1: TdxLayoutItem;
      dxLayoutItem2: TdxLayoutItem;
      dxLayoutGroup1: TdxLayoutGroup;
      procedure cxButton1Click(Sender: TObject);
      procedure btnCreateChildClick(Sender: TObject);
      procedure BitBtn3Click(Sender: TObject);
      procedure FormCreate(Sender: TObject);
      procedure FormActivate(Sender: TObject);
      procedure cxDBTimeEdit1Enter(Sender: TObject);
   private
      { Private declarations }
      ChildArray: array of Tfrm_Child;
      ChildArraySize: integer;
      ContainerActualWidth: integer;
      ContainerActualHeigt: integer;
   public
      { Public declarations }
   end;

var
   Form2: TForm2;

implementation


procedure TForm2.btnCreateChildClick(Sender: TObject);
begin
      ChildArraySize := (ChildArraySize+1);
      SetLength(ChildArray, ChildArraySize);
      ChildArray[ChildArraySize-1] := Tfrm_child.Create(Form2);
      ChildArray[ChildArraySize-1].Align := alLeft;
      ChildArray[ChildArraySize-1].Parent := cxScrollbox;
      ChildArray[ChildArraySize-1].AlignWithMargins := true;
      ChildArray[ChildArraySize-1].pnlchild.Color := clRed;
      ChildArray[ChildArraySize-1].pnlchild.Caption := 'ArrayINDEX ='+(ChildArraySize-1).ToString;
      ChildArray[ChildArraySize-1].Show;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
   ChildArraySize := 0;
   ContainerActualWidth := cxScrollbox.Width;
   ContainerActualHeigt := cxScrollbox.Height;
end;

Geändert von Int3g3r ( 5. Mär 2019 um 14:04 Uhr)
  Mit Zitat antworten Zitat
Alt 5. Mär 2019, 12:38     Erstellt von mkinzler
Dieser Beitrag wurde von mkinzler gelöscht.
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.292 Beiträge
 
Delphi 12 Athens
 
#2

AW: Wie erstelle ich dynamische forms richtig ?

  Alt 6. Mär 2019, 07:19
Moin...
Zitat:
ressourcensparend
...und DevExpress ist ein Widerspruch in sich selbst.
Zum Thema:
Im Prinzip machst du es richtig. Ich würde aber statt dem array eine TObjectList<TForm> oder ein TObjectDictionary<string, TForm> nehmen.
Über die Freigabe der Forms kann man diskutieren. Du verwendest Form2 als Owner. Du könntest auch die Liste die Forms freigeben lassen. (OwnObjects). Das ist hilfreich wenn man einzelne Forms aus der Liste entfernt.
  Mit Zitat antworten Zitat
Int3g3r

Registriert seit: 28. Nov 2018
Ort: Schweiz
118 Beiträge
 
Delphi 10.3 Rio
 
#3

AW: Wie erstelle ich dynamische forms richtig ?

  Alt 6. Mär 2019, 07:26
Moin...
Zitat:
ressourcensparend
...und DevExpress ist ein Widerspruch in sich selbst.
Zum Thema:
Im Prinzip machst du es richtig. Ich würde aber statt dem array eine TObjectList<TForm> oder ein TObjectDictionary<string, TForm> nehmen.
Über die Freigabe der Forms kann man diskutieren. Du verwendest Form2 als Owner. Du könntest auch die Liste die Forms freigeben lassen. (OwnObjects). Das ist hilfreich wenn man einzelne Forms aus der Liste entfernt.
Ja DevExpress ist sehr überladen bei vielen Komponenten, bieten aber einen guten support und die Komponenten laufen stabil.

Könntest du mir ein Beispiel schreiben wie du das meinst mit der TObjectList und TObjectDictionary ?
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.292 Beiträge
 
Delphi 12 Athens
 
#4

AW: Wie erstelle ich dynamische forms richtig ?

  Alt 6. Mär 2019, 07:32
Zitat:
Komponenten laufen stabil
"Element '' hat kein übergeordnetes Fenster" habe ich 30 Mal am Tag. Stabil ist was anderes.
Zitat:
Könntest du mir ein Beispiel schreiben wie du das meinst mit der TObjectList und TObjectDictionary
...mache ich dir.
Hausaufgaben:
http://docwiki.embarcadero.com/Libra...ns.TObjectList
http://docwiki.embarcadero.com/Libra...jectDictionary
Frage:
Wo ist der Unterschied zwischen den beiden?
  Mit Zitat antworten Zitat
Int3g3r

Registriert seit: 28. Nov 2018
Ort: Schweiz
118 Beiträge
 
Delphi 10.3 Rio
 
#5

AW: Wie erstelle ich dynamische forms richtig ?

  Alt 6. Mär 2019, 08:25
Wo ist der Unterschied zwischen den beiden?
TObjectList ist eine Liste von TObjects, wenn ich aus dieser Liste ein Object entfernte wird es automatisch freigegeben.
TObjectDictionary überprüft automatisch ob ein TObject noch gebraucht wird oder nicht und gibt dies automatisch frei wenn es nicht mehr gebraucht wird.

Hmm... also ist TObjectList ein Array von Objekten mit dem unterschied wenn ich etwas aus der TObjectList entferne wird es auch automatisch freigegeben.
Mit meinem Array oben bin ich selber verantwortlich das ALLE TObjects wieder freigegeben werden die ich erstellt habe.

Korrekt ?
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.292 Beiträge
 
Delphi 12 Athens
 
#6

AW: Wie erstelle ich dynamische forms richtig ?

  Alt 6. Mär 2019, 08:40
Zitat:
TObjectList ist eine Liste von TObjects, wenn ich aus dieser Liste ein Object entfernte wird es automatisch freigegeben.
Richtig, wenn du es willst. Bei Nein verhällt sich die Liste wie eine TList. Du kannst nur über den Index auf das Objekt zugreifen.
Zitat:
TObjectDictionary überprüft automatisch ob ein TObject noch gebraucht wird oder nicht und gibt dies automatisch frei wenn es nicht mehr gebraucht wird.
Nicht ganz. TDictionary verhält sich, wenn man es möchte, wie eine TObjectList. ABER über den Key (string, Integer, TBlubb was auch immer) kannst du dir das zugehörige Objekt direkt holen.

Delphi-Quellcode:
unit Unit2;

interface

uses
  System.Classes, System.Generics.Collections, System.Generics.Defaults, System.SysUtils,
  Vcl.Forms, Vcl.Controls, Vcl.StdCtrls;
  form_child; // :-) entspricht nicht dem Styleguide...besser FormChild


type
  TForm2 = class(TForm)
    btnCreateChild: TButton;
    lblList: TLabel;
    lblDict: TLabel;
    lblDict2: TLabel;
    procedure btnCreateChildClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
   private
      FList: TObjectList<TForm>;
      FDictionary: TObjectDictionary<string, TForm>;
   public

   end;

var
   Form2: TForm2;

implementation

procedure TForm2.FormCreate(Sender: TObject);
begin
  FList := TObjectList<TForm>.Create(True); // True gibt die Objekte selbstständig frei
  FDictionary := TObjectDictionary<string, TForm>.Create([doOwnsValues]); // doOwnValues gibt die Objekte (Value) selbstständig frei
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  FList.Free;
  FDictionary.Free;
end;

procedure TForm2.btnCreateChildClick(Sender: TObject);
var
  I: Integer;
  Child: TForm;
  TempForm: TForm;
begin
  for I := 0 to 2 do
  begin
    Child := Tfrm_child.Create(nil); // :-) entspricht nicht dem Styleguide...besser TfrmChild
    Child.Name := 'Child' + IntToStr(I);
    Child.Align := alLeft;
    Child.Parent := cxScrollbox;
    Child.AlignWithMargins := True;
    Child.pnlchild.Color := clRed;
    Child.pnlchild.Caption := 'ArrayINDEX = ' + IntToStr(Integer);

    FList.Add(Child);
    FDictionary.Add(Child.Name, Child);

    Child.Show;
  end;


  lblList.Caption := 'Name des ersten Eintrages: ' + FList[0].Name;
  lblDict.Caption := 'Name des 2. Eintrages im Dictionary: ' + FDictionary.Items['Child2'].Name;

  // Wenn man nicht weiß ob der Schlüssel existiert
  FDictionary.TryGetValue('Child2', TempForm);
  if Assigned(TempForm) then
  begin
    lblDict.Caption := 'Name des 3. Eintrages im Dictionary: ' + TempForm.Name;
  end
  else
  begin
    lblDict.Caption := 'Name des 3. Eintrages im Dictionary: nicht gefunden';
  end;
end;

end.
Delphi-Quellcode:
object Form2: TForm2
  Left = 0
  Top = 0
  Caption = 'Form2'
  ClientHeight = 299
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object lblList: TLabel
    Left = 48
    Top = 96
    Width = 26
    Height = 13
    Caption = 'lblList'
  end
  object lblDict: TLabel
    Left = 48
    Top = 120
    Width = 28
    Height = 13
    Caption = 'lblDict'
  end
  object lblDict2: TLabel
    Left = 48
    Top = 144
    Width = 34
    Height = 13
    Caption = 'lblDict2'
  end
  object btnCreateChild: TButton
    Left = 40
    Top = 40
    Width = 75
    Height = 25
    Caption = 'Create'
    TabOrder = 0
    OnClick = btnCreateChildClick
  end
end

Geändert von haentschman ( 6. Mär 2019 um 08:49 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von uligerhardt
uligerhardt

Registriert seit: 19. Aug 2004
Ort: Hof/Saale
1.735 Beiträge
 
Delphi 2007 Professional
 
#7

AW: Wie erstelle ich dynamische forms richtig ?

  Alt 6. Mär 2019, 08:49
Zitat:
Komponenten laufen stabil
"Element '' hat kein übergeordnetes Fenster" habe ich 30 Mal am Tag. Stabil ist was anderes.
Da machst du was falsch. Die Lernkurve ist zwar steil, aber das Zeug läuft bei uns rund.
Uli Gerhardt
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.292 Beiträge
 
Delphi 12 Athens
 
#8

AW: Wie erstelle ich dynamische forms richtig ?

  Alt 6. Mär 2019, 08:57
Zitat:
Da machst du was falsch
leider nein. DevExpress ist inzwischen auch der Meinung. Das liegt an den Inline Editoren in den Grids und dem WM_ACTIVATE. Aktuell habe ich einen Fix zum Testen. Der Fehler ist nicht neu:
https://www.google.de/search?source=...30.vHgVKlnGPMU
Zitat:
exec. date/time : 2019-03-02 08:20
version : 1.5.1.100
compiled with : Delphi 10.1 Berlin
madExcept version : 4.0.19
callstack crc : $1ce86e39, $0608bbf3, $83ece221
exception number : 5
exception class : EInvalidOperation
exception message : Element '' hat kein übergeordnetes Fenster.

main thread ($11f4):
006ee6b4 +0b4 Blubb.exe Vcl.Controls 9457 +13 TWinControl.CreateWnd
009767da +022 Blubb.exe cxControls 8288 +3 TcxControl.CreateWnd
006eec3e +016 Blubb.exe Vcl.Controls 9638 +3 TWinControl.CreateHandle
00a36667 +087 Blubb.exe cxContainer 3911 +12 TcxContainer.CreateHandle
00acad82 +00a Blubb.exe cxEdit 7745 +1 TcxCustomEdit.CreateHandle
00b17bde +00a Blubb.exe cxDropDownEdit 2613 +1 TcxCustomDropDownEdit.CreateHandle <-- Der Verursacher
006f2c20 +01c Blubb.exe Vcl.Controls 12238 +4 TWinControl.HandleNeeded
006f2c17 +013 Blubb.exe Vcl.Controls 12237 +3 TWinControl.HandleNeeded
006f2c2d +005 Blubb.exe Vcl.Controls 12244 +1 TWinControl.GetHandle
006b9fe0 +054 Blubb.exe Vcl.Forms 5965 +11 TCustomForm.SetWindowFocus
006ba100 +090 Blubb.exe Vcl.Forms 6001 +12 TCustomForm.SetActive
006babd6 +03a Blubb.exe Vcl.Forms 6384 +6 TCustomForm.WMActivate <-- Der Auslöser
006eb08a +2be Blubb.exe Vcl.Controls 7313 +91 TControl.WndProc
006efbd5 +5e9 Blubb.exe Vcl.Controls 10143 +158 TWinControl.WndProc
006b7859 +64d Blubb.exe Vcl.Forms 4523 +209 TCustomForm.WndProc
006ef1f4 +02c Blubb.exe Vcl.Controls 9850 +3 TWinControl.MainWndProc
0054ebe0 +014 Blubb.exe System.Classes 17187 +8 StdWndProc
006c0edb +04b Blubb.exe Vcl.Forms 10517 +6 TApplication.ProcessMessage
006c0fc6 +00a Blubb.exe Vcl.Forms 10564 +1 TApplication.HandleMessage
006c12f9 +0c9 Blubb.exe Vcl.Forms 10702 +26 TApplication.Run
03b5ca45 +d5d Blubb.exe Blubb 336 +215 initialization

Geändert von haentschman ( 6. Mär 2019 um 09:02 Uhr)
  Mit Zitat antworten Zitat
Andreas L.

Registriert seit: 23. Mai 2011
Ort: Furth im Wald
308 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: Wie erstelle ich dynamische forms richtig ?

  Alt 6. Mär 2019, 09:05
Du musst die Größe des Arrays nicht als Feld/Variable im Form "speichern".

Die Funktion Low liefert den ersten Index des Arrays. High den letzten. Length gibt die Größe aus. Mit Length kannst du übrigens auch die Länge eines Strings ermitteln. Ein String ist intern auch nur ein Array.

Delphi-Quellcode:
   TForm2 = class(TForm)
      ...
      procedure btnCreateChildClick(Sender: TObject);
      procedure FormCreate(Sender: TObject);
   private
      ChildArray: array of Tfrm_Child;
      ContainerActualWidth: integer;
      ContainerActualHeigt: integer;
   end;

...


procedure TForm2.btnCreateChildClick(Sender: TObject);
var
  Index: Integer;
begin
  // Array vergrößern
  SetLength(ChildArray, Length(ChildArray) + 1);

  // Letzten und damit aktuellen Index temporär "speichern"
  // damit nicht ständig High aufgerufen werden muss.
  Index := High(ChildArray);

  // Form erstellen
  ChildArray[Index] := Tfrm_child.Create(Form2);
  ChildArray[Index].Parent := cxScrollbox;
  ChildArray[Index].Align := alLeft;
  ChildArray[Index].AlignWithMargins := True;
  ChildArray[Index].pnlchild.Color := clRed;
  ChildArray[Index].pnlchild.Caption := 'ArrayINDEX =' + IntToStr(Index);
  ChildArray[Index].Show;
end;
Ich würde statt dem Array aber eine TObjectList verwenden.

Delphi-Quellcode:
uses
    ..., ContNrs;

  ...

   TForm2 = class(TForm)
      ...
      procedure btnCreateChildClick(Sender: TObject);
      procedure FormCreate(Sender: TObject);
      procedure FormDestroy(Sender: TObject);
      procedure FormClose(Sender: TObject);
   private
      FChildList: TObjectList;
   protected
      procedure CreateChildList;
      procedure DestroyChildList;
      function NewChildForm: Integer;
   end;

...

procedure TForm2.CreateChildList;
begin
  // Liste erstellen
  // Wird bei Create kein Parameter oder True übergeben
  // verwaltet die Liste alle eingetragenen Objekte
  // d. h. wenn die Liste freigegeben oder ein Eintrag
  // gelöscht wird, wird die / das Form automatisch
  // freigegeben
  FChildList := TObjectList.Create;
  // Wenn du False übergibst bist du selbst
  // verantwortlich die einzelnen Forms/Einträge
  // freizugeben:
  //FChildList := TObjectList.Create(False);
end;

procedure TForm2.DestroyChildList;
begin
  // Wenn OwnsObjects False ist,
  // musst du durch alle Einträge iterieren
  // und die Objekte selbst freigeben bevor
  // die Liste zerstört wird. Das würde so aussehen:
  for FormIndex := FChildList.Count - 1 downto 0 do
    FreeAndNil(FChildList[FormIndex]);
  // Da in CreateChildList nicht False übergeben wurde
  // ist dies nicht nötig.

  FreeAndNil(FChildList);
end;

function TForm2.NewChildForm: Integer;
var
  NewForm: Tfrm_child;
begin
  // Form erzeugen
  NewForm := Tfrm_child.Create(Self);
  try
    // Form der Liste hinzufügen
    // Add gibt den Index des neuen Eintrags zurück.
    Result := FChildList.Add(NewForm);

    // Form Eigenschaften setzen
    NewForm.Parent := cxScrollbox;
    NewForm.Align := alLeft;
    NewForm.AlignWithMargins := True;
    NewForm.pnlchild.Color := clRed;
    NewForm.pnlchild.Caption := 'ArrayINDEX =' + IntToStr(Result);
    NewForm.Show;
  except
    // Wenn ein Fehler auftritt wird -1 zurückgegeben.
    // Sinnvoller wäre es den Fehler zu "verarbeiten".
    Result := -1;
  end;
end;

procedure TForm2.btnCreateChildClick(Sender: TObject);
var
  Index: Integer;
begin
  // Funktion aufrufen um Form zu erstellen
  // Die Funktion gibt den Index des neuen Forms
  // zurück. Den kannst du evtl. brauchen wenn nachträglich
  // das Form geändert werden soll.
  // Wird -1 zurückgegeben konnte das Form nicht erstellt oder
  // nicht der Liste hinzugefügt werden.
  Index := Self.NewChildForm;

  // Überprüfen ob das Form erstellt
  // wurde, ggf. Meldung anzeigen.
  if Index = -1 then
    ShowMessage('Das Form konnte nicht erstellt werden!')
  else
    ShowMessage('Das Form wurde erfolgreich erstellt.');

  // Auf das Form via Index zugreifen
  Tfrm_child(FChildList[Index]).Caption := 'Hallo Welt';
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  // Liste erzeugen
  Self.ChildListCreate;
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  // Liste freigeben wenn das Form zerstört wird.
  // Das kann man alternativ auch im OnClose machen.
  Self.ChildListDestroy;
end;
Andreas Lauß
Blog

Geändert von Andreas L. ( 6. Mär 2019 um 09:13 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.292 Beiträge
 
Delphi 12 Athens
 
#10

AW: Wie erstelle ich dynamische forms richtig ?

  Alt 6. Mär 2019, 09:47
Moin...
Zitat:
Ich würde statt dem Array aber eine TObjectList verwenden.
...aber dann bitte zeitgemäß mit Generics. TObjectList<T>
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 3  1 23      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:19 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