[GELÖST] Dynamisch erzeugte Komponenten freigeben

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
OLLI_S
Beiträge: 65
Registriert: Di 17. Jan 2012, 20:55

[GELÖST] Dynamisch erzeugte Komponenten freigeben

Beitrag von OLLI_S »

Hallo,

ich suche eine Möglichkeit dynamisch erzeugte Komponenten wieder freizugeben.
Aber dabei stoße ich auf einen Fehler.

Auf meinem Formular habe ich eine ScrollBox1.
Ich habe eine Stringliste, in der die Texte für meine Komponenten stehen (ca. 10 bis 20 Einträge, Stringliste wird beim Erzeugen des Formulars gefüllt).
In diese Stringliste lese ich die Unterverzeichnisse eines bestimmten Verzeichnisses ein.
Auf der Scrollbox wird für jeden Eintrag in der Stringlsite ein Label und zwei Buttons erzeugt:

Code: Alles auswählen

procedure TfrmMainForm.CreateVisualComponents;
 
var
  counter: integer;
  aButtonLaunch: TBitBtn;
  aButtonDelete: TBitBtn;
  aLabel: TLabel;
 
begin
 
  // Gehe alle Elemente in derStringListe durch
  for counter := 0 to ( MeineStringListe.Count - 1 ) do
  begin
 
    // Label für die Bezeichnung
    aLabel := TLabel.Create ( ScrollBox1 );
    with aLabel do
    begin
      Parent := ScrollBox1;
      left := 10;
      Top := 13 + ( counter * 35 );
      Caption := MeineStringListe.Strings[counter];
    end;
 
    // Button für das Starten der Anwendung
    aButtonLaunch := TBitBtn.Create ( ScrollBox1 );
    with aButtonLaunch do
    begin
      Parent := ScrollBox1;
      Left := 300;
      Top := 5 + ( counter * 35 );
      Tag := Counter;
      Caption := 'Starte Anwendung';
      OnClick := @btnStartApplicationClick;
    end;
 
    // Button für das Löschen des Verzeichnisses
    aButtonDelete := TBitBtn.Create ( ScrollBox1 );
    with aButtonDelete do
    begin
      Parent := ScrollBox1;
      Left := 400;
      Top := 5 + ( counter * 35 );
      Tag := Counter;
      Caption := 'Lösche Verzeichnis';
      OnClick := @btnDeleteDirectory_Click;
    end;
 
  end;
 
end;
Das Erzeugen der Komponenten sowie die Aktionen bei OnClick fonktionieren.
Das ist nicht mein Problem.

Wenn ich den Button zum Löschen klicke, wird das entsprechende Verzeichnis auf der Festplatte gelöscht (auch das funktioniert).
Ebenso wird der entsprechende Eintrag aus der Stringliste entfernt.

Nun muss ich aber die dynamisch erzeugten Komponenten neu generieren.
Ich dachte, es ist einfacher alle Komponenten zu löschen und die Liste neu aufzubauen (zumal ich die Funktion zum Aufbau der Liste bereits habe).

Methode zum Löschen aller Elemente, in der zu Testzwecken anstelle dem Löschen erst mal die Caption verändert wird:

Code: Alles auswählen

procedure TfrmMainForm.DeleteVisualComponents;
 
var
  counter: integer;
  Komponente: Tcontrol; // TComponent;
 
begin
 
   for counter := 0 to ( ScrollBox1.ControlCount - 1 ) do // ComponentCount - 1 ) do
  begin
 
    Komponente := ScrollBox1.Controls[counter]; //  Components[counter];
 
     if Komponente is TLabel then
      ( Komponente as TLabel ).Caption := '--DELETE--';
    if Komponente is TBitBtn then
      ( Komponente as TBitBtn ).Caption := '--DEL--';
 
  end; 
 
end;
Das ganze funktioniert, die Bezeichner werden geändert.
Damit erreiche ich alle Kompoenten auf der Scrollbox.

Wenn ich anstelle der Caption zu ändern die Komponente lösche:

Code: Alles auswählen

if Komponente is TLabel then
  ( Komponente as TLabel ).Destroy;
if Komponente is TBitBtn then
  ( Komponente as TBitBtn ).Destroy;
dann erhalte ich eine Exception 'Class External: SIGSEGV'
Wenn ich anstelle "Destroy" ein "Free" verwende, erhalte ich die gleiche Exception.
Ich habe auch anstelle von TControl mal TComponent verwendet, das hat auch nicht funktioniert (daher die Kommentare oben).

Meine Fragen:
Was ist der Unterscheid zwischen Destroy und Free?
Was muss ich nehmen: TComponent oder TControl?
Was mache ich an meinem Code falsch?
Leider habe ich in Lazarus keine F1 Hilfe, sonst hätte ich schon nachgeschaut.

Danke für Eure Antworten!

Gruß

OLLI
Zuletzt geändert von OLLI_S am Mi 18. Jan 2012, 08:16, insgesamt 1-mal geändert.

Benutzeravatar
theo
Beiträge: 10872
Registriert: Mo 11. Sep 2006, 19:01

Re: Dynamisch erzeugte Komponenten freigeben

Beitrag von theo »

Code: Alles auswählen

for counter := 0 to ( ScrollBox1.ControlCount - 1 ) do 
  begin
     Komponente := ScrollBox1.Controls[counter];
Wenn du löschst, werden es ja immer weniger.
Wenn es 10 waren, dann löschst du am Ende die ScrollBox1.Controls[10], welche aber schon lange weg ist.
Du kannst entweder immer die 0 Löschen, oder von hinten beginnen also von ControlCount - 1 dowto 0

OLLI_S
Beiträge: 65
Registriert: Di 17. Jan 2012, 20:55

Re: Dynamisch erzeugte Komponenten freigeben

Beitrag von OLLI_S »

Hallo theo,

das Löschen des Eintrags passiert in der folgenden Methode:

Code: Alles auswählen

procedure TfrmMainForm.btnDeleteDirectory_Click ( Sender: TObject );
 
var
  Nummer: integer;
 
begin
 
  Nummer := ( Sender as TBitBtn ).Tag;
 
  // Lösche das Verzeichnis auf der Platte
  CleanUp_Directory ( fGlobalPath + MeineStringListe.Strings[Nummer], dmCompleteDirectory );
 
  // Zerstöre alle Elemente auf dem Formular
  // DeleteVisualComponents;
 
  // Lösche den Eintrag aus der Stringliste
  MeineStringListe.Delete ( Nummer );
 
  // Zeichne die Komponenten neu
  CreateVisualComponents;
 
end;
Der entsprechende Eintrag wird also aus der Stringliste gelöscht und dann die Werte neu gezeichnet.
In dem Listing siehst Du, dass die Methode "DeleteVisualComponents" (Löschen der Komponenten) momentan noch ausgegraut ist (nicht aufgerufen wird).

Das Löschen habe ich momentan in einem eigenen Button drinnen, bei dem nur die Methode "DeleteVisualComponents;" aufgerufen wird.
Damit kann ich die Methode "DeleteVisualComponents" alleine testen und sie anschließend an der entsprechenden Stelle einbauen.

Wenn der Button zum Lösche der Komponenten geklickt wird, ist das Formular längst aufgebaut.
Ich kann auf alle Komponenten zugreifen und die Caption ändern (siehe zweites Listing in meinem ersten Beitrag).
Dabei werden alle Elemente neu beschriftet (es funktioniert also).
Nur das zerstören funktioniert nicht.

Gruß

OLLI

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2813
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Dynamisch erzeugte Komponenten freigeben

Beitrag von m.fuchs »

Lies bitte noch einmal die Antwort von theo. Die Lösung steht dort drin.

Um es noch einmal deutlicher zu erklären, hier deine einzelnen Löschschritte (bei angenommenen zehn Labeln):
  • Noch nix gelöscht, das letzte Label ist Nummer 9.
  • Nummer 0 wird gelöscht, alle rücken eins vor, das letzte ist 8.
  • Nummer 1 wird gelöscht, alle rücken eins vor, das letzte ist 7.
  • Nummer 2 wird gelöscht, alle rücken eins vor, das letzte ist 6.
  • Nummer 3 wird gelöscht, alle rücken eins vor, das letzte ist 5.
  • Nummer 4 wird gelöscht, alle rücken eins vor, das letzte ist 4.
  • Nummer 5 soll gelöscht werden, aber das gibt es ja nicht mehr. => Fehler
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: Dynamisch erzeugte Komponenten freigeben

Beitrag von MAC »

ich kann dif folgendes empfehlen:
Du Legst 3 dynamische Arrays an, jeweils vom Typ TLabel (Beschriftung / TBitBtn (Starten) / TBitBtn (Loeschen);
dann schreibst du an den anfang

Code: Alles auswählen

setlength(BeschriftungsArray,MeineStringListe.Count);
setlength(StartenArray,MeineStringListe.Count);
setlength(LoeschenArray,MeineStringListe.Count);
 
// Gehe alle Elemente in derStringListe durch
for counter := 0 to ( MeineStringListe.Count - 1 ) do
  begin
 
  // aLabel := TLabel.Create ( ScrollBox1 );
  BeschriftungsArray[counter] := TLabel.Create ( ScrollBox1 );
  with  BeschriftungsArray[counter] do
     begin
     //...
     end;
 
  StartenArray[counter] := TBitBtn.Create ( ScrollBox1 );
  // usw. mit allen 3 sachen.
  end;
So jetzt hast du alle Buttons in einem Array.
Damit kannst du gezielt darauf zugreifen, z.B:

Code: Alles auswählen

BeschriftungsArray[2].Caption := 'Hier bin ich!';
oder auch alle löschen:

Code: Alles auswählen

for i := 0 to high(BeschriftungsArray) do BeschriftungsArray[i].Free;
for i := 0 to high(StartenArray) do StartenArray[i].Free;
for i := 0 to high(LoeschenArray) do LoeschenArray[i].Free;
setlength(BeschriftungsArray,0);
setlength(StartenArray,0);
setlength(LoeschenArray,0);
Allgemein empfehle ich dir folgendes:
a) verwende .Free stattt .Destroy
b) Das ganze würde sich auch Objektorientiert machen lassen, so dass du Beschrieftung / Start /Löschenbutton als Variable dieser Klasse nimmst und dann nur 1 Array von deiner neuen Klasse verwendest. Das würde es vom denken etwas komplexer machen, aber man könnte allgemeine Funktionen implementieren die Arbeit vereinfachen...

Code: Alles auswählen

Signatur := nil;

OLLI_S
Beiträge: 65
Registriert: Di 17. Jan 2012, 20:55

Re: Dynamisch erzeugte Komponenten freigeben

Beitrag von OLLI_S »

theo hat geschrieben:

Code: Alles auswählen

for counter := 0 to ( ScrollBox1.ControlCount - 1 ) do 
  begin
     Komponente := ScrollBox1.Controls[counter];
Wenn du löschst, werden es ja immer weniger.
Wenn es 10 waren, dann löschst du am Ende die ScrollBox1.Controls[10], welche aber schon lange weg ist.
Du kannst entweder immer die 0 Löschen, oder von hinten beginnen also von ControlCount - 1 dowto 0
Hallo theo,

sorry, ich hatte gestern Abend nach einem sehr langen Arbeitstag Deine Antwort falsch verstanden.
Ich dachte Du beziehst Dich auf die Stringliste.

Ja, Du hast Recht, die Komponenten auf dem Formular werden immer weniger.
Ich habe einfach die Schleife von ControlCount - 1 dowto 0 laufen lassen und jetzt funktioniert es.
Vielen Dank!

Gruß

OLLI

Antworten