Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Probleme mit dynamischen Komponenten


F34r0fTh3D4rk - Di 14.06.05 15:16
Titel: Probleme mit dynamischen Komponenten
Hallo :D

Ich erstelle zur Laufzeit Komponenten und möchte diese auch zur laufzeit wieder löschen, jetzt habe ich das problem, dass wenn die zuerst erstellte kompo gelöscht wird, kommt eine fehlermeldung, ebenfalls, wenn ich die top eigenschaft der restlichen bearbeiten will, hier mal code

Erstellung der Komponenten:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
procedure TMainForm.BTNAddEntryClick(Sender: TObject);
begin
(************************************
            ALsO
            ALsText
            ACheckboxes
            ABTNChange
            ABTNDelete
            AShape
************************************)


  SetLength(ALsO, Tag + 1);
  SetLength(ALsText, Tag + 1);
  SetLength(ACheckboxes, Tag + 1);
  SetLength(ABTNChange, Tag + 1);
  SetLength(ABTNDelete, Tag + 1);
  SetLength(AShape, Tag + 1);

  ALsO[tag] := Tlabel.Create(self);
  with ALsO[tag] do
    begin
      parent := SBToDo;
      top := Self.tag * 25;
      name := 'LO' + IntToStr(Self.tag + 1);
      with font do
        begin
          name := 'arial';
          style := [fsBold];
          size := 10;
        end;
      cursor := crhandpoint;
      caption := 'O';
      tag := self.tag + 1;
    end;
  ALsText[tag] := Tlabel.Create(self);
  with ALsText[tag] do
    begin
      parent := SBToDo;
      left := 15;
      top := Self.tag * 25;
      name := 'LText' + IntToStr(Self.tag + 1);
      with font do
        begin
          name := 'arial';
          size := 10;
        end;
      cursor := crhandpoint;
      caption := 'Ihr Text...';
      tag := self.tag + 1;
    end;
  ACheckboxes[tag] := Tcheckbox.Create(self);
  with ACheckboxes[tag] do
    begin
      parent := SBToDo;
      top := Self.tag * 25;
      left := 185;
      name := 'CBToDo' + IntToStr(Self.tag + 1);
      with font do
        begin
          name := 'arial';
          size := 10;
        end;
      caption := 'Fertig?';
      checked := false;
      tag := self.tag + 1;
    end;
  ABTNChange[tag] := Tbutton.Create(self);
  with ABTNChange[tag] do
    begin
      parent := SBToDo;
      top := Self.tag * 25;
      left := 245;
      width := 49;
      height := 17;
      name := 'BTNChange' + IntToStr(Self.tag + 1);
      caption := 'ändern';
      onclick := BTNChangeClick;
      tag := self.tag + 1;
    end;
  ABTNDelete[tag] := Tbutton.Create(self);
  with ABTNDelete[tag] do
    begin
      parent := SBToDo;
      top := Self.tag * 25;
      left := 300;
      width := 49;
      height := 17;
      name := 'BTNDelete' + IntToStr(Self.tag + 1);
      caption := 'löschen';
      onclick := BTNDeleteClick;
      tag := self.tag + 1;
    end;
  AShape[tag] := Tshape.Create(self);
  with AShape[tag] do
    begin
      parent := SBToDo;
      top := Self.tag * 25;
      left := 360;
      width := 15;
      height := 15;
      name := 'SStatus' + IntToStr(Self.tag + 1);
      brush.color := clred;
      shape := stcircle;
      tag := self.tag + 1;
    end;
  Tag := Tag + 1;
end;

Löschen der Komponenten:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
procedure TMainForm.BTNDeleteClick(sender : TObject);
var
  i: integer;
begin
  if sender is TButton then
    begin
      if (TButton(sender).tag) > 0 then
        begin
          ALsO[(TButton(sender).tag) - 1].free;
          ALsText[(TButton(sender).tag) - 1].free;
          ACheckboxes[(TButton(sender).tag) - 1].free;
          ABTNChange[(TButton(sender).tag) - 1].free;
          ABTNDelete[(TButton(sender).tag) - 1].free;
          AShape[(TButton(sender).tag) - 1].free;
          for i := TButton(sender).tag - 1 to length(ALsO) do
            ALsO[i].Top := ALsO[i].top - 25;

          for i := TButton(sender).tag - 1 to length(ALsText) do
            ALsText[i].Top := ALsText[i].top - 25;

          for i := TButton(sender).tag - 1 to length(ACheckboxes) do
            ACheckboxes[i].Top :=ACheckboxes[i].top - 25;

          for i := TButton(sender).tag - 1 to length(ABTNChange) do
            ABTNChange[i].Top := ABTNChange[i].top - 25;

          for i := TButton(sender).tag - 1 to length(ABTNDelete) do
            ABTNDelete[i].Top := ABTNDelete[i].top - 25;

          for i := TButton(sender).tag - 1 to length(AShape) do
            AShape[i].Top := AShape[i].top - 25;
        end;
    end;
end;


Lossy eX - Di 14.06.05 15:53

Wenn du die löscht wo verschiebst du denn dann da im deinem Array? Du musst natürlich alle Elemente darüber ein mal nach unten verschieben. Sonst ist das ziemlich gefährlich, da ja noch die Pointer von den nicht mehr existierenden dort gespeichert sind.

Aber mal was anderes. Wäre es bei solche einem Anwendungfall nicht sinnvoller mit einer TList oder TObjectList zu arbeiten. dadurch kannst du dir massiv arbeit ersparen. Zusätzlich dazu würde ich die Verwendung von TFrame empfehlen. Das könntest du auch gemütlich zur Laufzeit erstellen und hast somit deine Elemente auch ein wenig gruppiert. Dann kannst du die wesentlich einfacher verschieben oder löschen oder oder oder.


opfer.der.genauigkeit - Di 14.06.05 15:54

AAAALSOOOOOOOOOOOOOOOOOOOO

Fehlermeldung mit Namen nennen, wäre hilfreich. :mahn:

So wie das aussieht, solltest du mal nen Breakpoint setzen und
sowas wie Index + Arrayelement prüfen. (Access Violation Zeug eben)



Delphi-Quelltext
1:
2:
          for i := TButton(sender).tag - 1 to length(ALsO) do  
            ALsO[i].Top := ALsO[i].top - 25;


Hier greifst du zu und n paar Zeilen drüber


Delphi-Quelltext
1:
ALsO[(TButton(sender).tag) - 1].free;                    


Prüf das nochmals für die Fälle wie 1ter Eintrag und letzter Eintrag.

//EDIT: Too schlow


F34r0fTh3D4rk - Di 14.06.05 15:57

ja das -1 ist auch nur hier im forum vorhanden, aber der fehler ist der gleiche :lol: ich müsste dann die tag eigenschaft vom erstell button um 1 decreasen und im array alle nach unten verschieben und dann nochmal setlength machen 8)

Fehler besteht weiterhin:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
procedure TMainForm.BTNDeleteClick(sender : TObject);
var
  i: integer;
begin
  if sender is TButton then
    begin
      if (TButton(sender).tag) > 1 then
        begin
          ALsO[(TButton(sender).tag) - 1].free;
          ALsText[(TButton(sender).tag) - 1].free;
          ACheckboxes[(TButton(sender).tag) - 1].free;
          ABTNChange[(TButton(sender).tag) - 1].free;
          ABTNDelete[(TButton(sender).tag) - 1].free;
          AShape[(TButton(sender).tag) - 1].free;
          for i := TButton(sender).tag to length(ALsO) do
            begin
              ALsO[i].Top := ALsO[i].top - 25;
              ALso[i - 1] := ALso[i];
            end;

          for i := TButton(sender).tag to length(ALsText) do
            begin
              ALsText[i].Top := ALsText[i].top - 25;
              ALsText[i - 1] := ALsText[i];
            end;

          for i := TButton(sender).tag to length(ACheckboxes) do
            begin
              ACheckboxes[i].Top :=ACheckboxes[i].top - 25;
              ACheckboxes[i - 1] := ACheckboxes[i];
            end;

          for i := TButton(sender).tag to length(ABTNChange) do
            begin
              ABTNChange[i].Top := ABTNChange[i].top - 25;
              ABTNChange[i - 1] := ABTNChange[i];
            end;

          for i := TButton(sender).tag to length(ABTNDelete) do
            begin
              ABTNDelete[i].Top := ABTNDelete[i].top - 25;
              ABTNDelete[i - 1] := ABTNDelete[i];
            end;

          for i := TButton(sender).tag to length(AShape) do
            begin
              AShape[i].Top := AShape[i].top - 25;
              AShape[i - 1] := AShape[i];
            end;
            
          BTNAddEntry.Tag := BTNAddEntry.Tag - 1;

          SetLength(ALsO, Tag + 1);
          SetLength(ALsText, Tag + 1);
          SetLength(ACheckboxes, Tag + 1);
          SetLength(ABTNChange, Tag + 1);
          SetLength(ABTNDelete, Tag + 1);
          SetLength(AShape, Tag + 1);

        end;
    end;
end;

und der fehler ist mein lieblingfehler, nämlich: Format %p ungültig :)


Lossy eX - Di 14.06.05 16:05

Du könntest das zum Bleistifft auch mit einer TList lösen. Dann werden die automatisch verschoben und du hast diesen Verwaltungsaufwand nicht. Aber dazu solltest du die irgendwie gruppieren. Um die 6 Listen auf eine einzige herunter zubrechen. Dafür bieten sich wie schon erwähnt das TFrame an. Damit sind solchen Anwendungfälle ein Kinderspiel. Und du kannst deine Elemente zur Designzeit entwerfen. Vor allem musst du den Index der Elemente nicht im Tag speichern. Wobei ich den Sinn dahinter sowieso nicht so ganz verstehe.

PS: Kann Opfer nur zustimmen. Eine etwas genauere Fehlerbeschreibung könnte auch nicht schaden.


opfer.der.genauigkeit - Di 14.06.05 16:05

Im übrigen empfehle ich die HIGH-Methode um das letzte Element zu bekommen.
Bei Length kannste dich bei dynamischen Arrays an das - 1 dahinter gewöhnen.

Fehlermeldung bitte? :wink:


F34r0fTh3D4rk - Di 14.06.05 16:06

mit high hatte ich probleme, fehlermeldung steht oben 8)
user profile iconLossy eX hat folgendes geschrieben:
Du könntest das zum Bleistifft auch mit einer TList lösen. Dann werden die automatisch verschoben und du hast diesen Verwaltungsaufwand nicht. Aber dazu solltest du die irgendwie gruppieren. Um die 6 Listen auf eine einzige herunter zubrechen. Dafür bieten sich wie schon erwähnt das TFrame an. Damit sind solchen Anwendungfälle ein Kinderspiel. Und du kannst deine Elemente zur Designzeit entwerfen. Vor allem musst du den Index der Elemente nicht im Tag speichern. Wobei ich den Sinn dahinter sowieso nicht so ganz verstehe.

PS: Kann Opfer nur zustimmen. Eine etwas genauere Fehlerbeschreibung könnte auch nicht schaden.

auf frame umzustellen, wäre jetzt glaube ich etwas viel arbeit, vielleicht das nächste mal.

den tag brauche ich hierfür:

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TMainForm.BTNChangeClick(sender : TObject);
begin
  if sender is TButton then
    ShowToDoInput(TButton(sender).tag);
end;


opfer.der.genauigkeit - Di 14.06.05 16:11

Wie Probleme?:


Delphi-Quelltext
1:
2:
3:
for i := low(ary) to high(ary) do begin
  //todo: schaffe schaffe
end;


Schick mir den gesamten Sourcecode, damit ich ihn debuggen kann *gg*

Ich fahr jetzt erst mal Heim, meld mich dann nochmal.
Aber bis dahin habt ihr das bestimmt schon gelöst. 8)


opfer.der.genauigkeit - Di 14.06.05 16:16

user profile iconF34r0fTh3D4rk hat folgendes geschrieben:

und der fehler ist mein lieblingfehler, nämlich: Format %p ungültig :)


Zeiger: Das Argument muß ein Zeigerwert sein. Der Wert wird in einen String mit acht Zeichen, der den hexadezimalen Wert des Zeigers darstellt, konvertiert.

??

Heim.. :)


Lossy eX - Di 14.06.05 16:17

user profile iconF34r0fTh3D4rk hat folgendes geschrieben:
auf frame umzustellen, wäre jetzt glaube ich etwas viel arbeit, vielleicht das nächste mal.

Mehr arbeit als sich mit solchen Verwaltungsproblemen rumplagen zu müssen? Das sind eigentlich Sachen die als ersten implementiert werden müssen. Mehr als es dir empfehlen kann ich nicht. ;-) Alles andere liegt bei dir.


PS: Du solltest deine editierten Teile auch solche kennzeichnen. Jedes zweite mal wenn ich einen Beitrag sehe ist da noch etwas hinzugekommen. Das ist doch reichlich verwirrend.


F34r0fTh3D4rk - Di 14.06.05 16:21

dachte es hätte keiner bemerkt :lol:

ich dachte erst nicht an frames usw, und es muss ja auch so gehen, von daher :roll:


SvenAbeln - Di 14.06.05 16:33

Sollten deine Schleifen nicht nur bis Length -1 laufen?

Wenn deine Arrays bei 0 beginnen liegt folgender Zugriff z.B. ausserhalb von dem Array.

Delphi-Quelltext
1:
  Als0[length(ALsO)]                    



Dieser Tip kam ja vorhin schon einmal
user profile iconopfer.der.genauigkeit hat folgendes geschrieben:
Im übrigen empfehle ich die HIGH-Methode um das letzte Element zu bekommen.
Bei Length kannste dich bei dynamischen Arrays an das - 1 dahinter gewöhnen.


F34r0fTh3D4rk - Di 14.06.05 16:39

irgendwie werden jetzt manchmal die falschen gelöscht und verschieben tut ers net:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
procedure TMainForm.BTNDeleteClick(sender : TObject);
var
  i: integer;
begin
  if sender is TButton then
    begin
      if (TButton(sender).tag) > 1 then
        begin
          ALsO[(TButton(sender).tag) - 1].free;
          ALsText[(TButton(sender).tag) - 1].free;
          ACheckboxes[(TButton(sender).tag) - 1].free;
          ABTNChange[(TButton(sender).tag) - 1].free;
          ABTNDelete[(TButton(sender).tag) - 1].free;
          AShape[(TButton(sender).tag) - 1].free;
          for i := TButton(sender).tag to high(ALsO) - 1 do
            begin
              ALsO[i].Top := (i - 1) * 25;
              ALso[i - 1] := ALso[i];
              Application.ProcessMessages;
            end;

          for i := TButton(sender).tag to high(ALsText) - 1 do
            begin
              ALsText[i].Top := (i - 1) * 25;
              ALsText[i - 1] := ALsText[i];
              Application.ProcessMessages;
            end;

          for i := TButton(sender).tag to high(ACheckboxes) - 1 do
            begin
              ACheckboxes[i].Top := (i - 1) * 25;
              ACheckboxes[i - 1] := ACheckboxes[i];
              Application.ProcessMessages;
            end;

          for i := TButton(sender).tag to high(ABTNChange) - 1 do
            begin
              ABTNChange[i].Top := (i - 1) * 25;
              ABTNChange[i - 1] := ABTNChange[i];
              Application.ProcessMessages;
            end;

          for i := TButton(sender).tag to high(ABTNDelete) - 1 do
            begin
              ABTNDelete[i].Top := (i - 1) * 25;
              ABTNDelete[i - 1] := ABTNDelete[i];
              Application.ProcessMessages;
            end;

          for i := TButton(sender).tag to high(AShape) - 1 do
            begin
              AShape[i].Top := (i - 1) * 25;
              AShape[i - 1] := AShape[i];
              Application.ProcessMessages;
            end;
            
          BTNAddEntry.Tag := BTNAddEntry.Tag - 1;

          SetLength(ALsO, BTNAddEntry.Tag + 1);
          SetLength(ALsText, BTNAddEntry.Tag + 1);
          SetLength(ACheckboxes, BTNAddEntry.Tag + 1);
          SetLength(ABTNChange, BTNAddEntry.Tag + 1);
          SetLength(ABTNDelete, BTNAddEntry.Tag + 1);
          SetLength(AShape, BTNAddEntry.Tag + 1);

        end;
    end;
end;


SvenAbeln - Di 14.06.05 17:10

Hallo,

du verwendest die Tags um die Position im Array zu ermitteln,
daher mußt du die Tags auch Updaten wenn du die Position der Objekte im Array veränderst.

Ansonsten löscht du beim zweiten Löschen ein falsches Objekt.
Das Objekt mit Tag=4 steht nicht mehr im Array an Stelle 3 sondern jetzt auf 2, wenn ein davor liegendes gelöscht wurde.



Zitat:

und verschieben tut ers net:


Versuch mal dies:

Delphi-Quelltext
1:
              ALsO[i].Top := (i - 2) * 25;                    


F34r0fTh3D4rk - Di 14.06.05 19:28

wie date ich die tag up ?