Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Komponenten während der Laufzeit erstellen und löschen (https://www.delphipraxis.net/200414-komponenten-waehrend-der-laufzeit-erstellen-und-loeschen.html)

Pytroxis 17. Apr 2019 20:35

Komponenten während der Laufzeit erstellen und löschen
 
Guten Tag,

ich erstelle während der Laufzeit Labels, der Code ist hierfür:
Delphi-Quellcode:
  procedure CreateLabel (Form:TForm; Text:string; X,Y:Integer);
  var
    Lab : TLabel;
  begin
    Lab := TLabel.Create(Form);
    Lab.Parent := Form;
    Lab.Caption := Text;
    Lab.Left := X;
    Lab.Top := Y;
  end;
Die Labels werden auf einem 2. Form erstellt.
Wie kann ich es am geschicktesten anstellen, dass die Label gelöscht werden, sobald Form2 geschlossen wird und neu erstellt werden, sobald Form2 wieder aufgerufen wird?

Dalai 17. Apr 2019 20:54

AW: Komponenten während der Laufzeit erstellen und löschen
 
Solcherlei Erzeugung gehört ins OnCreate des Formulars, und dann kann/sollte die Zerstörung der Objekte im OnDestroy (oder OnClose) gemacht werden.

Andererseits setzt du den Owner des Labels bereits auf das Formular, und daher ist selbiges dafür zuständig, es wieder zu wegzuräumen, wenn das Formular zerstört wird.

Grüße
Dalai

DieDolly 17. Apr 2019 22:14

AW: Komponenten während der Laufzeit erstellen und löschen
 
Notfalls in eine Liste speichern und diese im FormClose durchgehen und alle Labels entfernen.

hoika 18. Apr 2019 07:04

AW: Komponenten während der Laufzeit erstellen und löschen
 
Hallo,
Zitat:

dass die Label gelöscht werden, sobald Form2 geschlossen wird
Da du als Owner das Form angegeben hast (Lab := TLabel.Create(Form)),
passiert das bereits im Destruktor des Forms.

siehe Dalai


Und zum Anlegen ist bereits alles gesagt.

peterbelow 18. Apr 2019 12:41

AW: Komponenten während der Laufzeit erstellen und löschen
 
Zitat:

Zitat von Pytroxis (Beitrag 1430524)
Guten Tag,

ich erstelle während der Laufzeit Labels, der Code ist hierfür:
Delphi-Quellcode:
  procedure CreateLabel (Form:TForm; Text:string; X,Y:Integer);
  var
    Lab : TLabel;
  begin
    Lab := TLabel.Create(Form);
    Lab.Parent := Form;
    Lab.Caption := Text;
    Lab.Left := X;
    Lab.Top := Y;
  end;
Die Labels werden auf einem 2. Form erstellt.
Wie kann ich es am geschicktesten anstellen, dass die Label gelöscht werden, sobald Form2 geschlossen wird und neu erstellt werden, sobald Form2 wieder aufgerufen wird?

Die Frage macht nur Sinn, wenn Du ein autocreated Form verwendest, das nach Benutzung nicht zerstört sondern nur versteckt wird (voreingestelltes Verhalten von TForm). Eigentlich sollte man nur das Mainform in der autocreate-Liste belassen und alle anderen Forms nach Bedarf erzeugen und auch konsequent wieder zerstören, wenn der Benutzer sie schließt. Naja, jedem Tierchen sein Pläsierchen :)

Deine Labels haben alle keine Namen, dadurch sind sie leicht zu identifizieren und von Labels zu unterscheiden, die im Designer erzeugt wurden. Du könntest Dir also eine weitere Hilfsfunktion schreiben, wie

Delphi-Quellcode:
  procedure ClearLabels (Form:TForm);
  var
    LComp: TComponent;
    I : integer;
  begin
    for I:= Form.ComponentCount -1 downto 0 do begin
      LComp := Form.Components[I];
      if (LComp is TLabel) and (LComp.Name = '') then
        LComp.Free;
    end;
  end;

hoika 18. Apr 2019 15:48

AW: Komponenten während der Laufzeit erstellen und löschen
 
Hallo,
Zitat:

Deine Labels haben alle keine Namen
Das hakte ich für ein Gerücht:
Delphi selbst vergibt einen Namen, wenn keiner angegeben wird.

Pytroxis 18. Apr 2019 22:11

AW: Komponenten während der Laufzeit erstellen und löschen
 
Zitat:

Zitat von peterbelow (Beitrag 1430564)
Delphi-Quellcode:
  procedure ClearLabels (Form:TForm);
  var
    LComp: TComponent;
    I : integer;
  begin
    for I:= Form.ComponentCount -1 downto 0 do begin
      LComp := Form.Components[I];
      if (LComp is TLabel) and (LComp.Name = '') then
        LComp.Free;
    end;
  end;

So ähnlich habe ich es auch gelöst, werde aber morgen mal deinen Ansatz probieren, da mir dieser besser erscheint :)


Das Problem ist also, soweit gelöst, allerdings habe ich eine weitere Frage:
Ich arbeite mit der Funktion Canvas und den Labels.
Das Zeichnen lasse ich in OnPaint (+ Canvas.Refresh) und OnResize stattfinden (sofern es eine bessere Möglichkeit gibt, bin ich gerne offen für Vorschläge).
Scheinbar werden nun aber andauernd die Labels erstellt (sie flackern) und teilweise, sind die Labels an Positionen wo sie nicht sein sollten.
Gibt es eine effektive Methode um zu verhindern, dass ganz viele Labels übereinander, an der gleichen Position erstellt werden?


Delphi-Quellcode:
procedure TForm2.FormPaint(Sender: TObject);
begin
  zeichnen(self, 1);
  Canvas.Refresh;
end;

procedure TForm2.FormResize(Sender: TObject);
begin
  DestroyLabel;
  Button1.Left := Round(ClientWidth * 0.0482954545454545);
  Button1.Top := Round(ClientHeight * 0.8140589569160998);
  Button1.Width := Round(ClientWidth * 0.9034090909090909);

  Canvas.Brush.Color := clMenu;
  Canvas.FillRect(Rect(0,0, Width, Height));
  zeichnen(self, 1);
end;
Stehe momentan ziemlich auf dem Schlauch :? :(

peterbelow 19. Apr 2019 09:39

AW: Komponenten während der Laufzeit erstellen und löschen
 
Zitat:

Zitat von hoika (Beitrag 1430579)
Hallo,
Zitat:

Deine Labels haben alle keine Namen
Das hakte ich für ein Gerücht:
Delphi selbst vergibt einen Namen, wenn keiner angegeben wird.

Nicht bei Komponenten, die man im Kode erzeugt. Es ist der Designer, der Namen erzeugt, nicht der Compiler.

peterbelow 19. Apr 2019 09:49

AW: Komponenten während der Laufzeit erstellen und löschen
 
[QUOTE=Pytroxis;1430599]
Zitat:

Zitat von peterbelow (Beitrag 1430564)
[DELPHI]
Ich arbeite mit der Funktion Canvas und den Labels.

Delphi-Quellcode:
procedure TForm2.FormPaint(Sender: TObject);
begin
  zeichnen(self, 1);
  Canvas.Refresh;
end;

procedure TForm2.FormResize(Sender: TObject);
begin
  DestroyLabel;
  Button1.Left := Round(ClientWidth * 0.0482954545454545);
  Button1.Top := Round(ClientHeight * 0.8140589569160998);
  Button1.Width := Round(ClientWidth * 0.9034090909090909);

  Canvas.Brush.Color := clMenu;
  Canvas.FillRect(Rect(0,0, Width, Height));
  zeichnen(self, 1);
end;
Stehe momentan ziemlich auf dem Schlauch :? :(

Du machst das irgendwie völlig falsch. Wenn Du variablen Text auf ein Form schreiben willst, mach das mit Canvas.TextOut oder Canvas.TextRect, nicht, indem Du TLabels auf das Form legst. Beides zu mischen macht keinen Sinn, denn TLabels sind persistent, wenn man sie einmal angelegt hat zeichnen sie sich selbst neu, wenn das notwendig ist. Wenn sich der Text ändert, den sie anzeigen sollen, ändert man die Caption-Eigenschaft des Labels und zerstört nicht das Label und erzeugt es neu.

Pytroxis 19. Apr 2019 16:16

AW: Komponenten während der Laufzeit erstellen und löschen
 
Zitat:

Zitat von peterbelow (Beitrag 1430613)
Du machst das irgendwie völlig falsch. Wenn Du variablen Text auf ein Form schreiben willst, mach das mit Canvas.TextOut oder Canvas.TextRect, nicht, indem Du TLabels auf das Form legst. Beides zu mischen macht keinen Sinn, denn TLabels sind persistent, wenn man sie einmal angelegt hat zeichnen sie sich selbst neu, wenn das notwendig ist. Wenn sich der Text ändert, den sie anzeigen sollen, ändert man die Caption-Eigenschaft des Labels und zerstört nicht das Label und erzeugt es neu.

Die Labels werden auch nur gelöscht/erstellt sofern die Form geschlossen/geöffnet wird, dies sollte auch so sein, da sich jedesmal der Inhalt, die Position und die Anzahl der Labels ändert.
Deine Idee mit dem Draw Text erscheint, mir allerdings wirklich als eine bessere alternative, so erspare ich mir einiges an schreib arbeit und außerdem scheint es mir damit einfacher zu sein ein dynamisches System zu erschaffen!

Edit: Durch den Vorschlag von Peterbelow konnte ich das gesamte System nun besser umsetzen, daher besteht das oben genannte Problem auch nicht mehr.
Nun hatte ich eine neue Idee, wobei ich wieder einige Probleme beim realisieren habe:
Beim zeichnen wird nicht nur ein Objekt sondern dynamisch viele (je nach Eingabe des Benutzers) erstellt (-> Kein Problem).
Natürlich werden ab einer bestimmten Anzahl, die Objekte außerhalb des Programmes gezeichnet.
Gibt es eine gute Möglichkeit zu scrollen?
Habe Online die Idee gesehen, dass man jedesmal die Objekte neuzeichnet und verschiebt je nach Stellung der Scrollbar aber um ehrlich zu sein habe ich keine Ahnung wie ich das am geschicktesten umsetzen soll, bzw. gibt es womöglich eine schönere/bessere alternative? :)

peterbelow 19. Apr 2019 17:47

AW: Komponenten während der Laufzeit erstellen und löschen
 
Zitat:

Zitat von Pytroxis (Beitrag 1430644)
Edit: Durch den Vorschlag von Peterbelow konnte ich das gesamte System nun besser umsetzen, daher besteht das oben genannte Problem auch nicht mehr.
Nun hatte ich eine neue Idee, wobei ich wieder einige Probleme beim realisieren habe:
Beim zeichnen wird nicht nur ein Objekt sondern dynamisch viele (je nach Eingabe des Benutzers) erstellt (-> Kein Problem).
Natürlich werden ab einer bestimmten Anzahl, die Objekte außerhalb des Programmes gezeichnet.
Gibt es eine gute Möglichkeit zu scrollen?
Habe Online die Idee gesehen, dass man jedesmal die Objekte neuzeichnet und verschiebt je nach Stellung der Scrollbar aber um ehrlich zu sein habe ich keine Ahnung wie ich das am geschicktesten umsetzen soll, bzw. gibt es womöglich eine schönere/bessere alternative? :)

Wenn Du eine Zeichenfläche brauchst, die größer als der Clientbereich des Forms ist und vom Benutzer scrollbar sein soll, mach das so:

Wirf eine TScrollbox auf das Form und setzte sein Align auf alClient. Wirf eine TPaintbox auf die Scrollbox und setzte ihre Top und Left auf 0.
Hänge einen Handler an den OnPaint-Event der Paintbox. Verschiebe den Kode, den Du jetzt im FormPaint Eventhandler hast, in diese Methode, aber Achtung!! Du mußt jetzt auf den Canvas der Paintbox zeichnen, nicht auf den des Forms!

Die letzte Aufgabe ist es dann, wenn sich die Daten ändern, zu berechnen, wie groß die Zeichenfläche dafür sein muß, und die Höhe (und ev.Breite) der Paintbox entsprechend zu setzen. Das feuert nicht nur automatisch den OnPaint-Event der Paintbox, sondern die Scrollbox zeigt auch automatisch Rollbalken an, wenn die Paintbox größer wird als die Client area der Scrollbox.

Pytroxis 22. Apr 2019 17:40

AW: Komponenten während der Laufzeit erstellen und löschen
 
Zitat:

Zitat von peterbelow (Beitrag 1430650)

Wenn Du eine Zeichenfläche brauchst, die größer als der Clientbereich des Forms ist und vom Benutzer scrollbar sein soll, mach das so:

Wirf eine TScrollbox auf das Form und setzte sein Align auf alClient. Wirf eine TPaintbox auf die Scrollbox und setzte ihre Top und Left auf 0.
Hänge einen Handler an den OnPaint-Event der Paintbox. Verschiebe den Kode, den Du jetzt im FormPaint Eventhandler hast, in diese Methode, aber Achtung!! Du mußt jetzt auf den Canvas der Paintbox zeichnen, nicht auf den des Forms!

Die letzte Aufgabe ist es dann, wenn sich die Daten ändern, zu berechnen, wie groß die Zeichenfläche dafür sein muß, und die Höhe (und ev.Breite) der Paintbox entsprechend zu setzen. Das feuert nicht nur automatisch den OnPaint-Event der Paintbox, sondern die Scrollbox zeigt auch automatisch Rollbalken an, wenn die Paintbox größer wird als die Client area der Scrollbox.

Das hatte super funktioniert, vielen Dank!

Könnt ihr den Fehler finden, weshalb die ListBox Items nicht geladen werden bzw. weshalb dieses leer bleibt?
Delphi-Quellcode:
var zahlen: array of TStrings;
...
//In einer Procedure die ausgeführt wird und welche funktioniert
zahlen[ComboBox1.ItemIndex-1] := ListBox1.Items;
...
procedure TForm1.Button5Click(Sender: TObject);
begin
  ListBox1.Items := zahlen[ComboBox1.ItemIndex-1];
end;
Finde ehrlich gesagt nicht den Fehler, länge des Arrays wurde gesetzt, etc.

EWeiss 22. Apr 2019 17:55

AW: Komponenten während der Laufzeit erstellen und löschen
 
Zitat:

Könnt ihr den Fehler finden, weshalb die ListBox Items nicht geladen werden bzw. weshalb dieses leer bleibt?
Du solltest sie schon addieren oder?

ListBox1.Items.add

PS:
Nebenbei! Neue Frage neuer Thread..

gruss

peterbelow 23. Apr 2019 12:43

AW: Komponenten während der Laufzeit erstellen und löschen
 
Zitat:

Zitat von Pytroxis (Beitrag 1430766)
Könnt ihr den Fehler finden, weshalb die ListBox Items nicht geladen werden bzw. weshalb dieses leer bleibt?
Delphi-Quellcode:
var zahlen: array of TStrings;
...
//In einer Procedure die ausgeführt wird und welche funktioniert
zahlen[ComboBox1.ItemIndex-1] := ListBox1.Items;
...
procedure TForm1.Button5Click(Sender: TObject);
begin
  ListBox1.Items := zahlen[ComboBox1.ItemIndex-1];
end;
Finde ehrlich gesagt nicht den Fehler, länge des Arrays wurde gesetzt, etc.

Tja, das Problem liegt vermutlich in deinem (mangelnden) Verständnis, wie Objekte funktionieren. Objekte sind Referenz-Typen, wenn man ein Objekt einer Variablen (oder dem Element eines Arrays) zuweist wird dabei nicht der Inhalt des Objektes kopiert, sondern seine Referenz (Addresse).

Delphi-Quellcode:
var zahlen: array of TStrings;
Ein SetLength(zahlen, N) liefert hier erstmal nur Speicherplatz für N Referenzen, die alle auf Nil gesetzt sind. Der Array enthält also keine Objekte.

Delphi-Quellcode:
//In einer Procedure die ausgeführt wird und welche funktioniert
zahlen[ComboBox1.ItemIndex-1] := ListBox1.Items;
...
Jetzt hast Du ein Element im Array, das eine Referenz auf das Items-Objekt der Listbox enthält. Der Eintrag zeigt also auf das selbe Objekt wie Listbox1.Items. Du hast also keine Kopie des Inhalts der Liste erstellt, wie Du vermutlich vor hattest.
Wenn Du jetzt den Inhalt der Listbox änderst verweist auch die Referenz in deinem Array auf die geänderte Liste.

Delphi-Quellcode:
procedure TForm1.Button5Click(Sender: TObject);
begin
  ListBox1.Items := zahlen[ComboBox1.ItemIndex-1];
end;
Und da wird es ganz übel. Die Zuweisung an die Items-Eigenschaft ruft die Setter-Funktion auf, und die löscht zunächst mal den Inhalt der Items-Liste. Dummerweise löscht sie damit auch den Inhalt der Liste in zahlen[ComboBox1.ItemIndex-1], denn das ist ja die selbe Liste...

Um wirklich den Inhalt der Listbox zu speichern mach das so:


Delphi-Quellcode:
var zahlen: array of String;

zahlen[ComboBox1.ItemIndex-1] := ListBox1.Items.Text;

ListBox1.Items.Text := zahlen[ComboBox1.ItemIndex-1];
Das ist am einfachsten, da der Compiler die Speicherverwaltung für Strings automatisch macht. Man könnte zwar auch TStringlist-Objekte als Ablage des Listeninhalts verwenden, aber das ist deutlich aufwendiger, den diese Objekte mußt Du erst erzeugen und im Array ablegen, Du mußt ihre Assign-Methode verwenden, um den Inhalt von Listbox1.Items in eine solche TStringlist zu kopieren, und natürlich mußt Du die Objekte wieder löschen, wenn Du sie nicht mehr brauchst. Wenn sich die Länge des Arrays ändern kann wird das aufwendig, da mußt Du höllisch aufpassen, damit keine Speicherlecks entstehen (wenn der Array schrumpft) oder TStringlist-Objekte fehlen (wenn der Array wächst, die neuen Elemente sind zunächst nil). In einem solchen Fall wäre eine TObjectlist (oder TObjectlist<TStringlist>) besser geeignet als ein array, die übernimmt zumindestens einen Teil der Speicherverwaltung.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:22 Uhr.

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