Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   ListView Data befüllen? (https://www.delphipraxis.net/192944-listview-data-befuellen.html)

SneakyBagels 4. Jun 2017 15:01


ListView Data befüllen?
 
Ich möchte gerne meinen Code etwas besser gestalten.
Ich habe eine ListView und aktuell füge ich einfach nur neue Einträge hinzu und greife auf die Caption zu.

Ich möchte das gerne über eine Liste regeln denn das hätte auch den netten Effekt, dass ich an anderer Stelle im Programm auf das Laden aus einer ini-Datei verzichten könnte.

Reicht hier eine TObjectList wo ich jedes erstellte Object einfach so dranhänge( teils Pseudo-Code):
Delphi-Quellcode:
aItem := ListView1.Items.Add;
aItem.Caption := 'ABC';
aItem.SubItems.Add('123');

// Hier nun ein Item für die ObjectList erzeugen und ...
aItem.Data := MeinObjectListItem;
Später greife ich dann auf MeineObjectList[irgendeinindex].irgendeinproperty zu. Ist das richtig so?
Acha das alles noch zusammen mit einem Record ist ja klar.

Edit1
Hier mal wie ich es aktuell teste!
Delphi-Quellcode:
type
 PListViewData_Record = ^TListViewData_Record;

 TListViewData_Record = packed record
  Caption: string;
 end;

// Button1
var
 aListViewObjectList_Record_Item: PListViewData_Record;
begin
 New(aListViewObjectList_Record_Item);
 aListViewObjectList_Record_Item.Caption := 'Caption-' + IntToStr(Random(123));
 ListView1.Selected.Data := aListViewObjectList_Record_Item;
end;

// Button2
ShowMessage(PListViewData_Record(ListView1.Selected.Data).Caption);

// Button3
var
 i: Integer;
begin
 for i := ListView1.Items.Count - 1 downto 0 do
  if ListView1.Items[i].Data <> nil then
   Dispose(PListViewData_Record(ListView1.Items[i].Data));
Edit2
Hier meine neue Version. Vielleicht ist die besser?

Delphi-Quellcode:
type
 PListViewData_Record = ^TListViewData_Record;

 TListViewData_Record = class
  Caption: string;
 end;

var
 aListViewData_List: TList<TListViewData_Record>;

// Liste erzeugen und Eintrag hinzufügen
var
 aListViewObjectList_Record_Item: TListViewData_Record;
begin
 // Liste erzeugen
 if aListViewData_List = nil then
  aListViewData_List := TList<TListViewData_Record>.Create;

 // Object erzeugen (Caption und alle SubItems) und an Data hängen
 aListViewObjectList_Record_Item := TListViewData_Record.Create;
 aListViewObjectList_Record_Item.Caption := ListView1.Selected.Caption;
 ListView1.Selected.Data := aListViewObjectList_Record_Item;

 // Objekt der Liste hinzufügen
 aListViewData_List.Add(aListViewObjectList_Record_Item);
 ShowMessage('Added: ' + aListViewObjectList_Record_Item.Caption + sLineBreak + 'Index in TList: ' + IntToStr(aListViewData_List.Count - 1));
end;

// Data Caption anzeigen
begin
 // Test: Data Caption anzeigen
 if ListView1.Selected.Data <> nil then
  ShowMessage(TListViewData_Record(ListView1.Selected.Data).Caption);

// Alle Objekte und Liste löschen
var i: Integer;
begi
 for i := aListViewData_List.Count - 1 downto 0 do
  aListViewData_List.Items[i].Free;

 // Liste freigeben
 aListViewData_List.Free;
 aListViewData_List := nil;

// Einen Eintrag in der Liste finden
 function getIndexInList(sCaption: string): SmallInt;
 var
  i: Integer;
 begin
  Result := -1;
  for i := 0 to aListViewData_List.Count - 1 do
   if AnsiSameText(aListViewData_List.Items[i].Caption, sCaption) then
    Result := i;
 end;

var
 iIndex: Integer;
begin
 iIndex := getIndexInList('123456');
 if (iIndex > -1) and (iIndex <= aListViewData_List.Count - 1) then
  ShowMessage('Caption: ' + aListViewData_List.Items[iIndex].Caption + sLineBreak + 'Index in TList: ' + IntToStr(iIndex));
end;

DeddyH 4. Jun 2017 16:41

AW: ListView Data befüllen?
 
Wozu der Record? Objektinstanzen sind ja auch Pointer, die kann man da direkt hineingeben. Ich hab mal schnell ein Beispiel zusammengeklickert, es werden die Standard-Komponentennamen verwendet:
Delphi-Quellcode:
unit Unit5;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Generics.Collections,
  Vcl.StdCtrls, Vcl.ComCtrls;

type
  TDingens = class
  private
    FName: string;
    FZahl: integer;
    procedure SetName(const Value: string);
    procedure SetZahl(const Value: integer);
  public
    property Name: string read FName write SetName;
    property Zahl: integer read FZahl write SetZahl;
  end;

  TForm5 = class(TForm)
    ListView1: TListView;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
    FListe: TObjectList<TDingens>;
  public
    { Public-Deklarationen }
  end;

var
  Form5: TForm5;

implementation

{$R *.dfm}

{ TDingens }

procedure TDingens.SetName(const Value: string);
begin
  FName := Value;
end;

procedure TDingens.SetZahl(const Value: integer);
begin
  FZahl := Value;
end;

procedure TForm5.Button1Click(Sender: TObject);
var
  Dingens: TDingens;
  Item: TListItem;
begin
  for Dingens in FListe do
    begin
      Item := ListView1.Items.Add;
      Item.Caption := Dingens.Name;
      Item.SubItems.Add(Dingens.Zahl.ToString);
      Item.Data := Dingens;
    end;
end;

procedure TForm5.FormCreate(Sender: TObject);
var
  Dingens: TDingens;
begin
  FListe := TObjectList<TDingens>.Create;

  Dingens := TDingens.Create;
  Dingens.Name := 'Hein Blöd';
  Dingens.Zahl := 42;
  FListe.Add(Dingens);

  Dingens := TDingens.Create;
  Dingens.Name := 'Max Mustermann';
  Dingens.Zahl := 100;
  FListe.Add(Dingens);

  Dingens := TDingens.Create;
  Dingens.Name := 'Klara Korn';
  Dingens.Zahl := 3;
  FListe.Add(Dingens);

  Dingens := TDingens.Create;
  Dingens.Name := 'Rudi Rüssel';
  Dingens.Zahl := 150;
  FListe.Add(Dingens);
end;

procedure TForm5.FormDestroy(Sender: TObject);
begin
  FListe.Free;
end;

end.
Jetzt sind die TDingens-Instanzen sowohl im Data-Feld des entsprechenden ListViewItems als auch in der Objektliste abgelegt. Das bedeutet dann aber, dass man nicht mehr auf Data zugreifen sollte, wenn die Liste bereits freigegeben wurde.

SneakyBagels 4. Jun 2017 16:48

AW: ListView Data befüllen?
 
Mein Edit 2 im ersten Beitrag sollte doch in etwa dem entsprechen was du geschrieben hast richtig?
Trennung des Codes kommt bei mir erst später. Das hier ist ja nur ein Test.

Sollte FListe.Free; bei dir nicht eigentlich MemoryLeaks für jedes TDingens hinterlassen?

DeddyH 4. Jun 2017 16:53

AW: ListView Data befüllen?
 
Ich weiß nicht, für mich sieht das recht umständlich aus. Ich würde erst einmal die Daten in der Objektliste ablegen und mich danach um die Darstellung kümmern. An das dem aktuellen ListViewItem zugeordneten Objekt kommst Du dann mit einem einfachen Typecast, wie Du es ja bereits tust.
Delphi-Quellcode:
var
  DeineKlasse: TDeineKlasse;
begin
  DeineKlasse := TDeineKlasse(ListView.Selected.Data);
  if Assigned(DeineKlasse) then
    DeineKlasse.MachWas;
Seit wann hinterlässt Free MemoryLeaks?

SneakyBagels 4. Jun 2017 17:06

AW: ListView Data befüllen?
 
Zitat:

Ich weiß nicht, für mich sieht das recht umständlich aus. Ich würde erst einmal die Daten in der Objektliste ablegen und mich danach um die Darstellung kümmern. An das dem aktuellen ListViewItem zugeordneten Objekt kommst Du dann mit einem einfachen Typecast, wie Du es ja bereits tust.
Wie gesagt das hier ist TEST-Code.
Ich muss das aber leider so machen aufgrund des Aufbaus meiner Schleife. Ich gehe die Schleife lieber einmal durch statt zweimal.
Vergess was ich da oben geschrieben habe.

Zitat:

Seit wann hinterlässt Free MemoryLeaks?
Wenn ich nur die Liste freigebe aber nicht die Objekte, dann erhalte ich einen MemoryLeak für jedes erstellte TListViewData-Class-Item.

Zitat:

Ich würde erst einmal die Daten in der Objektliste ablegen
ObjectList oder besser TList?

DeddyH 4. Jun 2017 17:09

AW: ListView Data befüllen?
 
Wenn Du tatsächlich MemoryLeaks willst, nimm TList, ich nehme lieber TObjectList ;)

SneakyBagels 4. Jun 2017 17:16

AW: ListView Data befüllen?
 
Ok diesen kleinen Unterschied kannte ich noch nicht. ich weiß zwar, dass TObjectList von TList kommt, aber dass letztes einen MemoryLeak erzeugt, wenn man die Einträge nicht explizit freigibt... wusste ich nicht.
Ich baue den Code heute Abend mal in mein Projekt ein und gucke was das wird.

Ziel soll es u.a. sein, dass ich nicht mehr direkt auf ListView Caption, SubItem usw zugreifen muss sondern das dann alles über die Liste machbar ist.
Dafür auch meine Funktion getIndexInList.

DeddyH 4. Jun 2017 17:22

AW: ListView Data befüllen?
 
Wenn Du über einen String in der Liste suchen willst, böte sich doch eine TStringList an. Da mit AddObject die Objekte rein, OwnsObjects auf true gesetzt, anschließend kannst Du mit IndexOf den passenden Eintrag ermitteln.

SneakyBagels 4. Jun 2017 17:29

AW: ListView Data befüllen?
 
Das mit der StringList ist echt eine Idee.
Aber dann hätte ich vermutlich wieder wildes Gecaste wie...
Delphi-Quellcode:
// mit StringList:
TListViewData(aListViewData_List.Objects[i]).Caption

// Mit TObjectList<TListViewData>
aListViewData_List.Items[i].Caption

DeddyH 4. Jun 2017 17:30

AW: ListView Data befüllen?
 
Das verstehe ich jetzt nicht, aber ich kenne ja auch Dein Programm nicht.

SneakyBagels 4. Jun 2017 17:33

AW: ListView Data befüllen?
 
Man muss es nicht kennen. Das hier ist wie gesagt Testcode und mehr gibt es aktuell nicht in diesem Testprojekt.
Wenn ich TStringList statt eine TObjectList<TListViewData> verwende, dann muss ich immer wenn ich auf ein Object von TStringList zugreife diesen Aufruf mit TListViewData() casten.
Das fällt bei der TObjectList<> eben weg.

DeddyH 4. Jun 2017 17:36

AW: ListView Data befüllen?
 
Achso, das liegt daran, dass es sich um eine generische Liste handelt, der Typ also von vornherein bekannt ist. Sofern es sich bei den Strings um eindeutige Werte handelt, käme auch noch ein TDictionary in Betracht, schneller geht es dann kaum noch. Allerdings muss man dann auch die Objekt selbst freigeben.

SneakyBagels 4. Jun 2017 17:39

AW: ListView Data befüllen?
 
Die Caption ist für jedes ListView-Item immer eindeutig. SubItems können jedoch gleiche Werte enthalten.
Beispiel

[ TestEintrag | C:\123 | Ja | Automatisch ]
[ EinAndererEintrag | C:\123 | Nein | Semi ]
[ RandomText | D:\exec | Nein | Automatisch ]

Alles, bis auf die erste Spalte, kann gleich sein.

DeddyH 4. Jun 2017 17:42

AW: ListView Data befüllen?
 
Dann könntest Du wie gesagt auch ein TDictionary nehmen mit einem String als Key und Deiner Klasse als Value.

SneakyBagels 4. Jun 2017 17:44

AW: ListView Data befüllen?
 
Das habe ich noch nie verwendet. Wie würde ich das in diesem Testfall denn deklarieren?
Die IDE schlägt mir vor
Delphi-Quellcode:
TDictionary<TKey,TValue>;
Mh schade IndexOf gibt es nicht =/

DeddyH 4. Jun 2017 17:48

AW: ListView Data befüllen?
 
Aus dem Kopf:
Delphi-Quellcode:
type
  TDeinForm = class(TForm)
  private
    FListe: TDictionary<string, TDeineKlasse>;

...

//Eintrag hinzufügen
FListe.AddOrSetValue(DasWasInDerCaptionSteht, DieInstanzDahinter);

//Eintrag auslesen
if FListe.TryGetValue(DasWasInDerCaptionSteht, DeineKlasse) then
  DeineKlasse.MachWas;

//Instanzen im Dictionary freigeben
for DeineKlasse in FListe.Values do
  DeineKlasse.Free;

SneakyBagels 4. Jun 2017 17:53

AW: ListView Data befüllen?
 
Wenn ich TryGetValue ausführe, wie komme ich denn dann an den Index des gefundenen Items heran?
Oder ist das gar nicht erst möglich, da das Dictionary mit Hashes arbeitet und wahrscheinlich eh sortiert ist?

DeddyH 4. Jun 2017 17:58

AW: ListView Data befüllen?
 
Es gibt keinen Index, und sortiert ist ein TDictionary auch nicht. Brauchst Du das unbedingt?

SneakyBagels 4. Jun 2017 18:03

AW: ListView Data befüllen?
 
Eigentlich nicht. Denn wenn ich mit TObjectList einmal das Item habe welches ich möchte, arbeite ich ja nur noch mit den dort hinterlegten Daten.
Ich baue gleich mal meinen Testcode mit TDictionary um und gucke wie es sich verhält.

Du sagtest "schneller geht es nicht".
Würde man überhaupt einen Unterschied zwischen TObjectList und TDictionary merken?
Es handelt sich hier um weniger als 50 ListView-Einträge.

DeddyH 4. Jun 2017 18:03

AW: ListView Data befüllen?
 
Bei der Menge wird der Unterschied nicht messbar sein, behaupte ich einfach mal. Aber wenn Du generell über Strings in Deiner Liste suchst, ist das IMO der bequemste Zugriff.

SneakyBagels 4. Jun 2017 18:27

AW: ListView Data befüllen?
 
Das scheint ganz gut zu funktionieren - jedenfalls im leeren Testprojekt. Ins große Projekt wird das heute Abend eingebaut.
Ich habe aber einige Stellen wo ich auf ListView-Items zugreife daher dauert die Umstellung auf Dictionary-TryGetValue ein bisschen.

Ich sehe du bist Fan von kurzgeratenen Schleifenköpfen. So ähnlich kenne ich die auch von PHP und mag die sehr.
In Delphi benutze ich die nicht. Wie baue ich das hier in eine normale Schleife mit Iterations-Variable i um?
Delphi-Quellcode:
//Instanzen im Dictionary freigeben
for DeineKlasse in FListe.Values do
  DeineKlasse.Free;
Ein einziges Item löschen, das möchte ich gerne wissen wie das mit dem Dictionary funktioniert.

Ach vergess den Quatsch da oben. Ich hab vergessen, dass es ja keinen Index gibt :oops:

haentschman 5. Jun 2017 05:25

AW: ListView Data befüllen?
 
Moin...:P
Delphi-Quellcode:
//Instanzen im Dictionary freigeben
for DeineKlasse in FListe.Values do
  DeineKlasse.Free;
Es gibt auch ein TObjectDictionary. Da wird beim Erzeugen des Dictionarys entschieden was mit den Objekten passiert.
http://docwiki.embarcadero.com/Libra...jectDictionary
http://docwiki.embarcadero.com/Libra...tionary.Create
Zitat:

Der Parameter Ownerships ist ein TDictionaryOwnerships-Typ, der angibt, ob das Dictionary Eigentümer der Schlüssel und/oder Werte in Einträgen ist. Das Dictionary kann entweder Eigentümer des Schlüssels, des Wertes, von beidem oder von keinem sein. Wenn das Dictionary zum Zeitpunkt des Entfernens des Objekts dessen Eigentümer ist, wird der Schlüssel und/oder der Wert freigegeben.

DeddyH 5. Jun 2017 07:59

AW: ListView Data befüllen?
 
Ach siehste, das hatte ich ganz verdrängt. :)

SneakyBagels 5. Jun 2017 10:12

AW: ListView Data befüllen?
 
Delphi-Quellcode:
// Dictionary erzeugen mit OwnsValues = True, damit Values bei Dictionary-Clear freigegeben werden
aListViewData_List := TObjectDictionary<string, TListViewData>.Create([doOwnsValues]);

// Dictionary leeren und alle Objekte freigeben
aListViewData_List.Clear;

// Dictionary freigeben
aListViewData_List.Free;
Und wieder zwei Codezeilen weniger. Da ich das Freigeben der Values sogar in eine Prozedur gepackt hätte sind es sechs Zeilen weniger.


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:49 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-2025 by Thomas Breitkreuz