Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Komponente aus Array löschen (https://www.delphipraxis.net/152106-komponente-aus-array-loeschen.html)

DeathsShadow 11. Jun 2010 11:56

Komponente aus Array löschen
 
Hallo :)

das Thema klingt simpel und wurde z.B. für Arrays of Integer schon sehr oft beantwortet, jedoch stellt es sich nicht ganz so leicht dar, weil meine Elemente initialisierte Komponenten sind. Folgendes Problem:

Ich habe in ein dynamisches Array of TShape wobei dessen Elemente während der Laufzeit manuell erstellt werden können. Den Elementen ist ein Popupmenu (Popupmenu2) zugeordnet, welches u.a. zum löschen eines Elements dient. Die Position des Elements im Array, wird dabei über den Tag bestimmt.

Nun möchte ich gerne ein Element an einer beliebigen Stelle löschen, wobei alle anderen Elemente im Array aufrücken müssen. Die Lösung über eine List ist aufgrund des restlichen Programmes leider nicht möglich. Hier ein Ansatz der (nicht funktioniert wobei er jedoch) meine Idee verdeutlicht.

Delphi-Quellcode:
  [...]

var
  Form1: TForm1;
  Btns : Array of TShape;

  [...]

procedure TForm1.Lschen2Click(Sender: TObject);
var i, Helper : Integer;
begin
  Helper := Popupmenu2.PopupComponent.Tag; //Wo ist das zu löschende Shape im Array

  [...]

  for i := helper to length(btns)-2 do //Alle Elemente um eins vorrücken(Das zu löschende SOLL überschreiben werden)
  begin
  btns[i] := btns[i+1];
  end;
 
  btns[length(btns)-1].Free; //letztes (doppeltes) Element wird gelöscht
  setlength(btns,length(btns)-1);
 
  for i := 0 to length(btns)-1 do btns[i].Tag := 1; // Tag wird wieder Position im Array

end;

[...]

end.
Ich hoffe auf eure Hilfe!

lg Flo

shmia 11. Jun 2010 13:36

AW: Komponente aus Array löschen
 
Viele Programmierer versuchen Objekte, Komponenten und Controls in Arrays zu speichern,
obwohl es viel geeignetere Datenstrukturen gibt.

Zitat:

Die Lösung über eine List ist aufgrund des restlichen Programmes leider nicht möglich
Dann solltest du das ändern!
Arrays sind low-Level Datenstrukturen.
Je anspruchsvoller die gewünschte Funktionalität eines Programms wird umso höherwertiger müssen auch die Datenstrukturen werden.

Z.B. bringt die Klasse TComponent schon alles mit was du brauchst.
Delphi-Quellcode:
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
    ShapeList : TComponent;  // Liste von Shapes
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   ShapeList := TComponent.Create(Self);
end;
Wichtig zu wissen: ShapeList wird automatisch freigegeben, wenn das Formular freigegeben wird.
Und ausserdem werden alle Shapes in der Liste ebenfalls automatisch freigegeben.

So kann man z.B. ein neues Shape hinzufügen:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
   newshape : TShape;
begin
   newshape := TShape.Create(ShapeList);
   newshape.Left := random(300);
   newshape.Top := random(300);
   newshape.Parent := Self;
end;
Um ein bestimmes Shape anhand des Tag zu finden und zu löschen:
Delphi-Quellcode:
procedure TForm1.DeleteShapeByTag(ATag:integer);
var
   c : TControl;
   i : integer;
begin
   for i := ShapeList.ComponentCount-1 downto 0 do
   begin
     c := ShapeList.Components[i];
     if c.Tag=ATag then
     begin
       // Shape löschen
       c.Free;
       // die ShapeList wird im Hintergrund automatisch korrigiert
       // d.h. ComponentCount verringert sich um eins
     end;
   end;
Anstatt in dem Tag des PopupMenue die Position in der Liste zu speichern, kann man auch gleich das Shape selbst speichern.

DeathsShadow 11. Jun 2010 16:24

AW: Komponente aus Array löschen
 
Danke für die Antwort, jedoch existieren dabei 3 Probleme.

Das erste ist, dass der Code einen sehr wichtigen Fehler enthält, so ist der Rückgabewert an c
Zitat:

Zitat von shmia (Beitrag 1028141)
Delphi-Quellcode:
c := ShapeList.Components[i];

vom Typ TComponent nicht von TControl.
Zitat:

Zitat von shmia (Beitrag 1028141)
Delphi-Quellcode:
c : TControl;

Ändert man dies und ersezt es durch TComponent und versucht ein Element zu löschen so kommt man zum zweiten Problem, es sind 2 Fälle möglich:

Fall1:
Man löscht Shape mit dem Index 0, dann werden alle Shapes gelöscht.

Fall2:
Man löscht ein Shape größer als Null, dann passiert nichts.


So ... das dritte und das schwerwiegenste Problem ist, dass ich selbst weiß wie es anders geht, jedoch wollte ich wissen wie es mit array funktioniert.



PS: Ist nicht böse gemeint, ich hoffe jemand hat noch eine Idee!

Klaus01 11. Jun 2010 16:56

AW: Komponente aus Array löschen
 
Hallo,

wenn es denn unbedingt mit Arrays sein soll,
würde ich so vorgehen:

Shape aus dem Ursprungsarray löschen.
Position auf nil setzen.

Neues Array erstellen mit der Länge des Ursprungsarrays -1.
Alle Elemente <> nil in das neue Array kopieren.
Ursprungsarray löschen.

Grüße
Klaus

Luckie 11. Jun 2010 17:05

AW: Komponente aus Array löschen
 
Warum mit zwei Arrays arbeiten? Einfach alle nachfolgenden Elemente um eins nach vorne kopieren. Vorher aber das zu löschende Objekt freigeben. Und danach natürlich das Array um ein Element verkürzen.
Muss die Sortierreihenfolge nicht erhalten bleiben, kann man auch das letzte Element an die Stelle des zu löschenden Elementes kopieren.

shmia 11. Jun 2010 17:15

AW: Komponente aus Array löschen
 
Zitat:

Zitat von DeathsShadow (Beitrag 1028196)
jedoch wollte ich wissen wie es mit array funktioniert.

Wenn im Array ein "Loch" entsteht, dann muss man halt die Elemente zusammenschieben.
Man kann das mit einer Schleife machen.
Hardcore - Programmierer setzen hier auch die Procedure Move ein.
Delphi-Quellcode:
procedure RemoveElementFromArray(a : Array of TShape; idx:integer);
var
  i : integer;
begin
  Assert(idx >= 0);
  for i := idx to High(a)-1 do
  begin
    a[idx] := a[idx+1];
  end;
  SetLength(a, Length(a)-1); // Array verkürzen
end;
Anschliesend musst du wohl auch noch noch die Tag-Werte von deinem Popupmenu korrigieren.
Delphi-Quellcode:
for i := 0 to Popupmenu2.Items.count-1 do
begin
  if Popupmenu2.Items[i].Tag >= Helper then
     Popupmenu2.Items[i].Tag := Popupmenu2.Items[i].Tag -1;
end;
Ich kann nur sagen, dass du so einen hässlichen Sourcecode bekommst.

Würdest du meinen Vorschlag annehmen, dann sähe das Löschen so aus:
Delphi-Quellcode:
procedure TForm1.Lschen2Click(Sender: TObject);
var
  mi : TMenuItem;
  c : TComponent;
begin
  mi := Sender as TMenuItem;
  // im Tag des Menuitems steckt die Verbindung zum Shape
  c := TComponent(mi.Tag);
  if not Assigned(c) then
     Exit;
  c.Free; // Shape wird gelöscht und automatisch auch aus ShapeList entfernt

DeathsShadow 11. Jun 2010 17:29

AW: Komponente aus Array löschen
 
@Klaus: Nette Idee aber eher die brachiale methode ^^

@Luckie: Guck dir mal an, was ich gemacht hab. Oder was meinst du?

@shima: Vielen Danke, aber bei deinem Vorschlag zum Thema Array, wird das Elemten was zu löschen ist, aus dem Array entfernt, aber nicht gelöscht. Das letzte Element im Array wird auch aus dem Array entfernt, jedoch auch nichts gelöcht. Hab ich mich vielleicht vertippt? Kannst du das bitte selbst probieren ob es bei dir klappt?


lg

Klaus01 11. Jun 2010 18:00

AW: Komponente aus Array löschen
 
.. schon mal überlegt was diese Zeile macht?

Delphi-Quellcode:
 for i := 0 to length(btns)-1 do btns[i].Tag := 1; // Tag wird wieder Position im Array
Grüße
Klaus

x000x 11. Jun 2010 18:22

AW: Komponente aus Array löschen
 
Moin moin,

ich würde auch Shimas Methode bevorzugen...
Delphi-Quellcode:
type
   TShapeArr = Array of TShape;
//..
procedure RemoveElementFromArray(var a : TShapeArr; Idx: Integer);
var
  I, x: Integer;
begin
   x := Length(a);
   if x > 0 then begin
      Assert((idx >= 0) and (idx < x));
      a[idx].Free;
      for I := Idx to x-2 do begin
         a[I] := a[Succ(I)];
      end;
      SetLength(a, x -1);
   end;
end;
So sollte es aber funktionieren.

xZise 13. Jun 2010 10:39

AW: Komponente aus Array löschen
 
Moin,
warum gibst du das letzte Element frei? Nicht der Inhalt sondern die Referenz wird bei der Aktion kopiert.
Das heißt die letzten beiden Einträge zeigen (vor den .Free) auf das gleiche Element. Durch das freigeben, wir das letzte Element aber freigegeben und zwar das letzte Element für „beide“ Arrays (also bevor du es verkürzt: Das letzte (was du evtl. willst) und das vorletzte (was du bestimmt nicht willst)).

Und warum setzt du den Tag für jedes Shape auf 1? Und auf das Popupmenü bekommt kein neues Tag, soll das so sein? Außerdem gibst du nicht (!) das gelöscht Element frei. Das verschwindet ins Nirvana, weil du einfach die Referenz mit den nachfolgenden Element überschreibst.

Das heißt, einfach Luckies Rat folgen und:
Zitat:

Zitat von Luckie (Beitrag 1028204)
[...]Vorher aber das zu löschende Objekt freigeben.[...]

Interessant finde ich diesen Hinweis:
Zitat:

Zitat von Luckie (Beitrag 1028204)
[...]Muss die Sortierreihenfolge nicht erhalten bleiben, kann man auch das letzte Element an die Stelle des zu löschenden Elementes kopieren.

Das ist noch einfacher zu implementieren.

MfG
Fabian


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:44 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