Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi String in Listview Data ? (https://www.delphipraxis.net/149791-string-listview-data.html)

Blamaster 30. Mär 2010 10:07


String in Listview Data ?
 
Hi,

ich möchte einen String im .Data eines Listviewitems hinterlegen.

Wie genau macht man das ?

Ich habe es gerade mal so versucht:

Delphi-Quellcode:
var
 test: string;
 item: TListItem;
begin
 test := 'Ich liege in der Listview.Items.Item.data'
 
 item := Listview.Items.Add;
 item.Caption := 'Irgendwas';
 item.Data := Pointer(test);
end;
Und dann zum auslesen:

Delphi-Quellcode:
ShowMessage(String(ListView.Items.Item[x].Data));
Dabei entstehen aber große Unregelmäßigkeiten. Mal steht der string im Data so wie er soll, mal nur merkwürdige Zeichen, mal garnichts, mal gibt es eine exception. Wo liegt mein Fehler ?

Mfg Yannic

IceBube 30. Mär 2010 10:15

Re: String in Listview Data ?
 
Hallo!

Also bei mir funktioniert das einwandfrei. Fehler sind bei mir nicht aufgetreten, auch wenn man nichts in die Data schreibt..kommt keine Exception...

lg

Neutral General 30. Mär 2010 10:17

Re: String in Listview Data ?
 
Der String darf nicht in einer lokalen Variable gespeichert sein. Dann sollte es gehen.

himitsu 30. Mär 2010 10:18

Re: String in Listview Data ?
 
Eventuell schlägt hier die Referenzzählung des Strings zu, wenn du den String über Pointer(...) zuweißt, wird da die Referenzzählung nicht behandelt.
Wird dann der übergebene String mal freigegeben, dann ist er weg und in .Data steht nur ein Zeiger zu einem nicht mehr existierendem String.

versuch es mal so:
Delphi-Quellcode:
//zuweisen
String(item.Data) := test;

// auslesen
test := String(item.Data);

Blamaster 30. Mär 2010 10:30

Re: String in Listview Data ?
 
@ himitsu

Delphi-Quellcode:
String(item.Data) := test;
Dabei bekomme ich einen Error "Left side cannot be assigned to"

Kann evtl. meine for schleife das Problem sein ?

Delphi-Quellcode:
for i := 0 to x do
begin
test := 'Ich liege in der Listview.Items.Item.data an der Stelle + ' ' + IntToStr(i);

item := Listview.Items.Add;
item.Caption := 'Irgendwas';
item.Data := Pointer(test);
end;
Ein Pointer ist ja ein Zeiger zeigt dieser nur zum zuweisen auf test, so dass der String danach fest im item.Data liegt ?

himitsu 30. Mär 2010 10:40

Re: String in Listview Data ?
 
tja, entweder zu sorgst dafür, daß "test" nie freigegeben/verändert wird, solange die Referenz in item.Data liegt,

oder du mußt eben doch die Referenzzählung beachten

Delphi-Quellcode:
var P: Pointer;

// zuweisen
String(P) := test;
item.Data := P;

// auslesen
test := String(item.Data);

// freigeben
P := item.Data;
String(P) := '';

oder du legst doch einen Record, mit deinem String, in item.Data ab


oder du gehst über einen PChar (natürlich mußt du dafür selber den Speicher reservieren und den Stringinhalt dareinkopieren), welchen du dann an item.Data übergibst.
(und freigeben nicht vergessen)

Blamaster 30. Mär 2010 11:40

Re: String in Listview Data ?
 
Hi,

sprich dein Beispielcode müsste in meinem Fall so aussehen ?

Delphi-Quellcode:
var
  P: Pointer;
  test: string;
  i: integer;
  item: TListItem;
begin
 for i := 0 to 10 do
 begin
  item := TListItem.Items.Add;
  item.caption := 'Test ' + IntToStr(i);
  test := 'Ich liege in der Listview.Items.Item.data an der Stelle ' + IntToStr(i);
  String(P) := test;
  item.Data := P;
 end;
end;
Wenn ja, dann geht es so auch nicht selbe Probleme wie in Post 1.

Mfg Yannic

hoika 30. Mär 2010 11:58

Re: String in Listview Data ?
 
Hallo,

ich nehme für meine TListItem.Data immer eine Klasse
die in einer Liste steckt.
Nat. darf die Liste nicht lokal sen, sondern gehört ins Form.

In deinem Fall würde auch eine TStringList reichen.

Wie schon weiter vorn gesagt, darf die String-Variable
nicht lokal sein.

Data ist ja nur ein Zeiger auf das Original.
wenn das Original weg ist, zeigt Data irgendwohin.


Heiko

Blamaster 30. Mär 2010 13:03

Re: String in Listview Data ?
 
Hi,

schade das Data so umständlich zu verwenden ist.

also nochmal zum verständnis. Data enthält einen pointer der auf die richtigen Daten zeigt. Wenn die Variable auf die Pointer zeigt nun so nicht mehr existiert gibt es Probleme logisch.

Was ändert sich dann aber durch eine globale Variable ? Diese existiert dann zwar immernoch nur wenn ich eine einfache Stringvariable habe und sich alle Datafelder auf diese beziehen, dann müssten doch alle den aktuellen Wert der Stringvariableenthalten oder nicht ?

So wie ich es verstehe:

- Data -> Enthält pointer auf Objekt -> Pointer verweist auf globale Variable test

Jetzt lese ich Data aus:

- Data liest seinen Pointer der ihm sagt wo er nachsehen muss sucht dann nach der variable test und liest sie aus.

Sprich wenn am Schluss wo es um die Ausgabe geht in der globalen Variable 'blub' steht, dann würde jedes durch die for schleife definierte Datafeld nun auf 'blub' zeigen oder nicht ?

Mit einer Progressbar geht das ganze recht einfach ind verständlich:

Delphi-Quellcode:
var
 pb: TProgessBar
 item: TListItem;
 i: integer;
begin
 for i := 0 to 10 do
 begin
  item := Listview.Items.Add
  item.Caption := 'Progressbar ' + IntToStr(i);
  pb := TProgressbat.Create(nil);
  item.Data := pb;
 end;
end;
Wieso lässt sich das mit einem string nicht auch so einfach erledigen ?

Wie genau war das mit mir würde auch eine Strnglist reichen gemeint ?

Mfg Yannic

himitsu 30. Mär 2010 13:24

Re: String in Listview Data ?
 
.Data ist nicht wirklich umständlich nutzbar ... es ist wie mit jedem anderen Zeiger/Pointer auch.
Und hier war nunmal die eigentlich gute Referenzzählung schuld, welche man durch solch einen Cast absichtlich umgeht.

Einziges Problem bei Properties ist, daß sie keinen gleichzeitigen Schreib- und Lesezugriff erlauben, weswegen String(item.Data):=irgendwas; nicht ging.



Die hoika schon sagte, wenn man auf was zeigen will, dann muß man dafür sorgen, daß dieses nicht in der Zwischenzeit freigegeben wird.
Wobei Objekte keine referenzzählung haben und man sie daher auch direkt, in einen Pointer gekastet, in Data eintragen könnte.

Delphi-Quellcode:
item.Data := Pointer(Test);
//auslesen
Test := PChar(item.Data);
//oder
Test := String(item.Data); // wenn Test ein String ist
ist praktisch das Selbe wie
Delphi-Quellcode:
item.Data := PChar(Test);
//auslesen
Test := PChar(item.Data);
außer daß bei PChar für einen Leerstring (nil) nicht nil, sondern ein Zeiger auf einen mit #0 gefüllten Speicherplatz übergeben wird.

Wird jetzt Test verändert, freigegeben oder verschoben, dann zeigt .Data natürlich immernoch auf die Stelle des "alten" Strings und somit teigt String(item.Data) nicht mehr auf einen String und es knallt oder es kommt im günstigsten Falle nur Schrott raus.

Delphi-Quellcode:
var
  P: Pointer;
  i: integer;
  item: TListItem;
begin
  for i := 0 to 10 do
  begin
    item := TListItem.Items.Add;
    item.caption := 'Test ' + IntToStr(i);
    String(P) := 'Ich liege in der Listview.Items.Item.data an der Stelle ' + IntToStr(i);
    item.Data := P;
  end;
end;
Delphi-Quellcode:
test := 'Ich liege in der Listview.Items.Item.data an der Stelle ' + IntToStr(i);
String(P) := test;
item.Data := P;
test := '';
Hier wird P wie eine Stringvariable behandelt, es wird also bei Zuweisung auch der Referenzzähler des String erhöht.
Gibt man nun test wieder frei, dann wird der Referenzzähler erniedrigt, aber da ja noch die Referenz für String(P) existiert, wird der String nicht freigegeben, sondern existiert weiterhin in P.
Die Folge ist nun, daß man beim löschen des Items auch den String in item.Data mit freigeben muß, da ja sonst ein Speicherleck entsteht.


Fazit:
Wird etwas direkt in .Data abgelegt, dann muß man es dort auch wieder freigeben,
auch wenn man ein Objekt nur da reinlegt.

Wärend bei hoikas Vorschlag das Objekt in einer externen Liste liegt und in P nur eine Kopie des Instanzzeigers.
Bei ihm muß/darf man das Objekt in .Data also nicht freigeben, da hierfür die Liste verantwortlich ist, welche ja quasi das Original besitzt.

Blamaster 30. Mär 2010 14:18

Re: String in Listview Data ?
 
Hi, Danke für die ausführliche Erklärung.

Aber was ist nun für mich der ja wirklich nur einen String im Datafeld hinterlegen will am besten zu nutzen ?

Bisher war ja egal bei welcher Variante immer das Problem, dass nur Datenmüll rauskam.

Mfg Yannic

hoika 30. Mär 2010 15:05

Re: String in Listview Data ?
 
Hallo,

TStringList ...

Zu deiner Meinung, dass es schwer zu benutzen ist.

In einer ListView zeige ich Daten aus einer Liste an.
Die Liste besteht bei mir meistens eh schon im Quelltext ...


Heiko

SirThornberry 30. Mär 2010 15:09

Re: String in Listview Data ?
 
Also Beispiel nehmen wir mal einen Integer
Delphi-Quellcode:
var
  meinInt: Integer;
  item: TListItem;
begin
  meinInt := 5;
  item := Listview.Items.Add;
  item.Caption := 'Irgendwas';
  item.Data := @meinInt;
end;
würdest du hier erwarten das nach zurückkehren der Funktion Data noch auf einen gültigen Wert zeigt obwohl meinInt eine lokale Variable ist die ihre Gültigkeit mit Verlassen der Funktion verliert?

Bei einem String ist es nicht anders. Wenn man will das Speicher bei Verlassen der Funktion nicht frei gegeben wird muss man dafür sorgen das er nicht im Gültigkeitsbereich der Funktion liegt. Man kann also entweder eine globale Variable nehmen oder dynamisch Speicher anfordern.

Delphi-Quellcode:
var
  meinInt: ^Integer;
  item: TListItem;
begin
  new(meinInt);
  meinInt^ := 5;
  item := Listview.Items.Add;
  item.Caption := 'Irgendwas';
  item.Data := meinInt;
end;
Vergessen sollte man auf keinen Fall, dass Speicher den man selbst anfordert auch selbst wieder frei geben sollte. Bevor du also dein Item löschst solltest du auch den Speicher worauf item.Data zeigt wieder freigeben.
Delphi-Quellcode:
var
  meinInt: ^Integer;
  item: TListItem;
begin
  [...]
  meinInt := item.Data;
  dispose(meinInt);
[Edit]doppeltes Wort entfernt und Komma anders gesetzt[/Edit]

Björn Ole 30. Mär 2010 15:16

Re: String in Listview Data ?
 
Ich mach das mit Strings so:

Delphi-Quellcode:
// hineinschreiben
var
  p: PChar;

p := StrAlloc(Length(myStr));
StrCopy(p, PChar(myStr));
AItem.Data := p;

// im OnDestroy des Formulars gehe ich alle Items durch
// und lösche deren Data-Strings
for i := 0 to ListView.Items.Count - 1 do
  StrDispose(PChar(ListView.Items[i].Data));
Ist daran etwas auszusetzen?

SirThornberry 30. Mär 2010 15:30

Re: String in Listview Data ?
 
Naja, PChar hat keine Längenangabe und daher wird #0 meist als Ende interpretiert. Das kann man umgehen indem man anstelle von PChar einfach ^String verwendet. Somit kann man durch dereferenzieren auch die Compilermagic etc. nutzen und hat keine Probleme wenn einmal #0 vorkommt (gerade bei UTF8, Unicode etc. sehr sinnvoll)
Dein Beispiel würde dann also so aussehen:
Delphi-Quellcode:
var
  p: ^String;

new(p);
p^ := myStr;
AItem.Data := p;
[...]
Somit kann man alles (abgesehen vom notwendigen dereferenzieren) wie gewohnt machen und muss nicht plötzlich auf Funktionen wie StrCopy zurück greifen welche genannte Nachteile haben. Das kopieren durch direkt Zuweisung könnte damit durchaus sogar schneller sein weil ein normaler String eine Längenangabe besitzt wo hingegen bei PChar erst nach der abschließenden #0 gesucht wird um zu wissen wie groß die zu kopierenden Daten sind.

Blamaster 30. Mär 2010 16:57

Re: String in Listview Data ?
 
Hi,

Nachdem SirThornberrys Vorschlag so auch nicht wirklich funktionieren wollte, (ich denke mal der Sehler war das ich dann immernoch mit string(Item.Data) auslesen wollte ?), habe ich nun einfach ein eigenes Objekt erstellt.

Dies kann ich nun endlich so handeln wie ich es auch von der Progressbar gewohnt war.

Delphi-Quellcode:
var
 TestObject: TTestObject;
 item: TListItem;
 i: integer;
begin
 
 for i := 0 to 10 do
 begin
  item := ListView.Items.Add;
  item.Caption := 'Eintrag Nr. ' + IntToStr(i);
  TestObject := TTestObject.Create;
  TestObject.String := 'String im Data des Item: ' + IntToStr(i);
  item.Data := TestObject;
 end;
 
end;

und das auslesen dann einfach über:

Delphi-Quellcode:
var
 MeinString: String
begin
 MeinString := TTestObject(ListView.Items.Item[i].Data).String;
 ShowMessage(MeinString);
end;
Funktioniert jetzt soweit auch alles super und hat sogar den Vorteil das ich um einiges flexibler bin.

Danke für all die Hilfe :)

Mfg Yannic

Neutral General 30. Mär 2010 17:00

Re: String in Listview Data ?
 
Aber nicht vergessen das Objekt wieder freizugeben! :wink:

himitsu 30. Mär 2010 17:01

Re: String in Listview Data ?
 
Mit einem eigenem Constructor dürfte wohl auch sowas gehn.
Delphi-Quellcode:
item.Data := TTestObject.Create('String im Data des Item: ' + IntToStr(i));

Blamaster 30. Mär 2010 17:43

Re: String in Listview Data ?
 
Hi,

zum freigeben habe ich nochmal eine Frage.

Muss ich das während der Laufzeit bei jedem Löschvorgang direkt machen, oder gibt es eine Möglichkeit beim beenden des Programms automatisch alle erstellten TTestObjecte freizugeben ?

Mfg Yannic

himitsu 30. Mär 2010 19:54

Re: String in Listview Data ?
 
In Delphi-Referenz durchsuchenOnDeletion würde ich einfach alle diese Klassen freigeben, also jeweils die Klasse des Items, welches grade manuel oder automatisch gelöscht wird.

Blamaster 30. Mär 2010 21:02

Re: String in Listview Data ?
 
Hi,

das war auch mein erster Gedanke, allerdings habe ich festgestellt das auch das verschieben von Listvieweinträgen irgendwie das ondeletion event auslöst und dann hat man bei dem verschobenen Eintrag kein data mehr.

Mfg Yannic

hoika 31. Mär 2010 07:21

Re: String in Listview Data ?
 
Hallo,

und wieder mein Vorschlag.
Packe alle erzeugten Objekte in eine Liste
und gebe die Liste und die Einträge dann beim Form-Ende frei.


Heiko

Blamaster 31. Mär 2010 09:45

Re: String in Listview Data ?
 
hi,

in was für einer Liste kann man Objekte ablegen ?

Mfg Yannic

himitsu 31. Mär 2010 12:46

Re: String in Listview Data ?
 
Zitat:

Zitat von Blamaster
in was für einer Liste kann man Objekte ablegen ?

TObjectList?


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:08 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz