![]() |
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:
Und dann zum auslesen:
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;
Delphi-Quellcode:
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 ?
ShowMessage(String(ListView.Items.Item[x].Data));
Mfg Yannic |
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 |
Re: String in Listview Data ?
Der String darf nicht in einer lokalen Variable gespeichert sein. Dann sollte es gehen.
|
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); |
Re: String in Listview Data ?
@ himitsu
Delphi-Quellcode:
Dabei bekomme ich einen Error "Left side cannot be assigned to"
String(item.Data) := test;
Kann evtl. meine for schleife das Problem sein ?
Delphi-Quellcode:
Ein Pointer ist ja ein Zeiger zeigt dieser nur zum zuweisen auf test, so dass der String danach fest im item.Data liegt ?
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; |
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) |
Re: String in Listview Data ?
Hi,
sprich dein Beispielcode müsste in meinem Fall so aussehen ?
Delphi-Quellcode:
Wenn ja, dann geht es so auch nicht selbe Probleme wie in Post 1.
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; Mfg Yannic |
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 |
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:
Wieso lässt sich das mit einem string nicht auch so einfach erledigen ?
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; Wie genau war das mit mir würde auch eine Strnglist reichen gemeint ? Mfg Yannic |
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:
ist praktisch das Selbe wie
item.Data := Pointer(Test);
//auslesen Test := PChar(item.Data); //oder Test := String(item.Data); // wenn Test ein String ist
Delphi-Quellcode:
außer daß bei PChar für einen Leerstring (nil) nicht nil, sondern ein Zeiger auf einen mit #0 gefüllten Speicherplatz übergeben wird.
item.Data := PChar(Test);
//auslesen Test := PChar(item.Data); 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:
Hier wird P wie eine Stringvariable behandelt, es wird also bei Zuweisung auch der Referenzzähler des String erhöht.
test := 'Ich liege in der Listview.Items.Item.data an der Stelle ' + IntToStr(i);
String(P) := test; item.Data := P; test := ''; 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. |
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 |
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 |
Re: String in Listview Data ?
Also Beispiel nehmen wir mal einen Integer
Delphi-Quellcode:
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?
var
meinInt: Integer; item: TListItem; begin meinInt := 5; item := Listview.Items.Add; item.Caption := 'Irgendwas'; item.Data := @meinInt; end; 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:
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.
var
meinInt: ^Integer; item: TListItem; begin new(meinInt); meinInt^ := 5; item := Listview.Items.Add; item.Caption := 'Irgendwas'; item.Data := meinInt; end;
Delphi-Quellcode:
[Edit]doppeltes Wort entfernt und Komma anders gesetzt[/Edit]
var
meinInt: ^Integer; item: TListItem; begin [...] meinInt := item.Data; dispose(meinInt); |
Re: String in Listview Data ?
Ich mach das mit Strings so:
Delphi-Quellcode:
Ist daran etwas auszusetzen?
// 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)); |
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:
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.
var
p: ^String; new(p); p^ := myStr; AItem.Data := p; [...] |
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:
Funktioniert jetzt soweit auch alles super und hat sogar den Vorteil das ich um einiges flexibler bin.
var
MeinString: String begin MeinString := TTestObject(ListView.Items.Item[i].Data).String; ShowMessage(MeinString); end; Danke für all die Hilfe :) Mfg Yannic |
Re: String in Listview Data ?
Aber nicht vergessen das Objekt wieder freizugeben! :wink:
|
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));
|
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 |
Re: String in Listview Data ?
In
![]() |
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 |
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 |
Re: String in Listview Data ?
hi,
in was für einer Liste kann man Objekte ablegen ? Mfg Yannic |
Re: String in Listview Data ?
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:00 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