Erstellen + Löschen von Komponenten zur Laufzeit

Liebe Delphi-Gurus,

Ich erstelle zur Laufzeit mehrere Labels(ein Menü, definiert in einer XML-Datei) auf dem Hauptformular und weisen diesen dabei ein onClick event zu, welches in einer Procedure im Hauptformular definiert ist. In dieser Procedure werden die Labels mit „Free“ gelöscht und andere Komponenten (wiederum definiert in einer XML-Datei) erstellt.
Das funktioniert soweit ohne Fehler.

Nun werden diese Komponenten im onClick Event eines „festen“ Buttons gelöscht und das vorher beschriebene Menü wird wieder aufgebaut.
Klicke ich wieder auf ein Label, wird das Menü wieder wie gewünscht gelöscht und die Komponenten werden wie gewünscht erstellt.
Anschießend gibt es jedoch eine Fehlermeldung, meistens „Zugriffsverletzung in Modul…“, manchmal „Abstakter Fehler“.
Alles in Delphi 5 Enterprise unter Windows 2000 oder Windows XP Professional SP1.

Welche Möglichkeit gibt es, diesen Fehler abzustellen. Gibt es einen anderen Ansatz?

Viele Grüße
Manfred

Hallo Manfred,

die Free-Methode läßt die Addresse in der Variablen zurück. Es hört sich so an, als versucht das Programm auf eine solche Addresse zuzugreifen. Das ist zB der Fall, wenn du mit TLabel(Sender).Free das Objekt freigibst und später über eine Variable auf die ehemalige Addresse dieses breits freigegebenen Objekts zuzugreifen versuchst.
Um das rauszufinden, solltest du nach jedem Aufruf von Free die Variable gleich NIL setzen und vor jedem Aufruf der Variable prüfen, ob sie ungleich NIL ist.

Nochmal an einem Beispiel (die wichtigen Stellen sind fett; den Tag benutze ich als Referenz auf den Index des TLabel-Arrays, wo die Addressen der Labels gespeichert sind):

type 
 TForm1 = Class(TForm)
 ...
 Label: Array[1..n] of TLabel;
 ...
 end;
 
Procedure Form1.MenuAufbauen;
var i:integer;
begin
 ...
 for i := 1 to n do 
 begin
 Label[i] := TLabel.Create(Form1);
**Label[i].Tag := i;**
 Label[i].OnClick := Form1.LabelOnClick;
 ...
 end;
 ...
end;
 
Procedure Form1.LabelOnClick;
begin
 ...
 Label[TLabel(Sender).Tag].Free;
**Label[TLabel(Sender).Tag] := NIL;**
 ...
end;
 
Procedure Form1.ButtonClick;
var i:integer;
begin
 ...
 for i := 1 to n do
 begin
 Label[i].Free;
**Label[i] := NIL;**
 end;
 MenuAufbauen;
 ...
end;

So sollte das eigentlich funktionieren. Wenn du nicht klar kommst, frag nochmal nach.

Gruß
Jochen

Hallo Jochen,
vielen Dank für Deine Antwort.
Leider funktioniert es nicht.
Nach dem Löschen der Menülabels wird nicht mehr auf sie zugegriffen.
Ich lösche das Menü in einer Schleife mit

((MainForm.ContentPanel.FindComponent(tmpStr)) as TLabel).Free;

In „tmpStr“ steht der Name des Labels.
Das funktioniert auch.
Ich habe jetzt

((MainForm.ContentPanel.FindComponent(tmpStr)) as TLabel) := Nil;

eingefügt.
Der Compiler sagt mir aber, der linken Seite kann nichts zugewiesen werden. Mache ich etwas falsch?

Viele Grüße
Manfred

Hallo Manfred,

Leider funktioniert es nicht.

-(

Ich lösche das Menü in einer Schleife mit

((MainForm.ContentPanel.FindComponent(tmpStr)) as
TLabel).Free;

Soweit ok.

((MainForm.ContentPanel.FindComponent(tmpStr)) as TLabel) :=
Nil;

kann nicht funktionieren. Du hast ja hier die Verwendung von Variablen umgangen. Wenn Du immer über FindComponent auf die Labels zugreifst, ist das mit NIL sowieso hinfällig.

Ich habe aber möglicherweise das Problem:

Wer ist denn der Owner deiner Labels? Ich denke, Owner ist Mainform und das ContentPanel ist der Parent. FindComponent sucht in der Componentenliste der Owners, und da findet es möglicherweise deine Labels garnicht. Also gibt es einen leeren Zeiger zurück, der natürlich aus „as Label“ betrachtet werden kann. Dummerweise funktioniert die Methode Free auch bei leeren Zeigern, so daß auch da kein Fehler ausgegeben wird. Kurz: Diese Anweisung

((MainForm.ContentPanel.FindComponent(tmpStr)) as TLabel) :=
Nil;

läuft fehlerfrei, macht aber effektiv garnichts!

Kannst du das mal überprüfen? zB mit

var Test: TLabel;
begin
 ... 
 test := MainForm.ContentPanel.FindComponent(tmpStr)) as TLabel;
 ShowMessage(Test.Name+': '+Test.Caption);
 test.Free;
 ...
end;

Wenn das nicht funktioniert, dann probier es mit

test := MainForm.FindComponent(tmpStr)) as TLabel;

Wenn es aber funktioniert, dann stehe ich auch auf dem Schlauch. Dann könntest du mir evtl. mal den Quelltext schicken.

Gruß
Jochen

Hallo Jochen,

vielen Dank für Deine Antwort.

Ich habe Deinen Vorschlag kurz ausprobiert. Nun werde ich mit „Ungültige Typumwandlung“ belästigt.
Da ich mich jetzt um ein anderes Projekt kümmern muss, kann ich hier im Moment nicht weiterforschen.
Darf ich Dich in den nächsten Tagen per E-Mail belästigen?
Wenn ich eine Lösung gefunden habe, werde ich sie hier posten.

Viele Grüße
Manfred

Hi nochmal,

Ich habe Deinen Vorschlag kurz ausprobiert. Nun werde ich mit
„Ungültige Typumwandlung“ belästigt.

Ich habe das bei mir auch probiert, und da ging das problemlos…

Ich sehe gerade, daß die Klammern nicht ganz stimmen (eine Klammer-auf fehlt oder ein Klammer-zu ist zu wenig. Tschuldigung. Sieh mal nach, ob’s daran lag. Ansonsten:

Darf ich Dich in den nächsten Tagen per E-Mail belästigen?

Ja.

Wenn ich eine Lösung gefunden habe, werde ich sie hier posten.

Das wäre super, danke.

Viele Grüße

dito
Jochen

Hallo Jochen,
vielen Dank für Deine Antwort.
Leider funktioniert es nicht.
Nach dem Löschen der Menülabels wird nicht mehr auf sie
zugegriffen.
Ich lösche das Menü in einer Schleife mit

bestimmt mit einer for i := 0 to componentcount do?
versuch mal eine for i := componentcount downto 0 do…

((MainForm.ContentPanel.FindComponent(tmpStr)) as
TLabel).Free;

In „tmpStr“ steht der Name des Labels.
Das funktioniert auch.
Ich habe jetzt

((MainForm.ContentPanel.FindComponent(tmpStr)) as TLabel) :=
Nil;

eingefügt.
Der Compiler sagt mir aber, der linken Seite kann nichts
zugewiesen werden. Mache ich etwas falsch?

Viele Grüße
Manfred

bestimmt mit einer for i := 0 to componentcount do?
versuch mal eine for i := componentcount downto 0 do…

oder noch besser mit „for i := componentcount –1 downto 0 do…“