![]() |
VirtualStringTree und Klasseninstanzen - Node Data finden
Ich versuche mir gerade eine Funktion zu schreiben die mir die Node-Daten anhand eines Index findet
Delphi-Quellcode:
Mein Problem besteht darin, dass ich nicht weiß ob ich mich am Index .Index oder [i] orientieren muss, um das zurückzugeben was ich suche.
function GetNodeData(Index: Integer): TUserdata;
var i: Integer; begin Result := nil; for i := 0 to _UserDataClasses.Count - 1 do if _UserDataClasses[i].Index = Index then begin Result := _UserDataClasses[i].aNode.GetData<TUserData>; Exit; end; end; In einer anderen Prozedur füge ich Klasseninstanzen in meine ObjectList hinzu und dachte, dass Folgendes funktioniert
Delphi-Quellcode:
Das .Insert( funktioniert leider überhaupt nicht wie gewollt. Das VST zeichnet mir den an beispielsweise Index 2 hinzugefügten Eintrag gar nicht und kopiert irgendwie nur den aller letzten.
if InsertAtPosition > -1 then
_UserDataClasses.Insert(InsertAtPosition, _UserData) else _UserDataClasses.Add(_UserData); Daher die Frage. Ist es vollkommen egal in welcher Reihenfolge die Daten in der ObjectList liegen [man sortiert das VST ja glaube ich eh manuell] und soll man sich nur am Index .Index (eigene Variable in der Klasse) orientieren den ich dann jetzt so setze
Delphi-Quellcode:
?
// Prozedur die Daten lädt, eine Klasseninstanz erstellt und in die ObjectList packt. Mit dem optionalen Parameter UseIndex kann man den Inhalt der Variablen Index selber bestimmen.
if UseIndex> -1 then _UserDataClasses.Index := UseIndex else _UserData.Index := _UserDataClasses.Count; _UserDataClasses.Add(_UserData); zweite Frage. Wie schaffe ich es die Funktion GetNodeData(Index: Integer) oben als überladene Funktion ohne doppelten Code bereitzustellen? Beispielsweise wenn ich als Parameter UserName: string haben und diesen auch finden möchte? Ich könnte die Funktion einfach kopieren und statt .Index = Index dann .UserName = Username prüfen. Aber das wäre doppelter Code. Bin für Hilfe sehr dankbar! Oder meine frage etwas anders formuliert. Wenn ich einen Nutzernamen finden möchte, iteriere ich dann durch die Nodes oder durch die ObjectList?
Delphi-Quellcode:
Irgendwie bin ich gerade total durcheinander weil ich absolut keine Ahnung habe, an welche Daten ich mich jetzt halten soll... Klasseninstanzen oder nicht?
// General helper function used in multiple other functions (e.g. DeleteUserNameNode)
function FindUserIDByName(const UserName: string): Integer; var i: Integer; begin Result := -1; if Assigned(_UserDataClasses) then begin for i := 0 to _UserDataClasses.Count - 1 do begin if AnsiSameText(_UserDataClasses[i].UserName, UserName) then begin Result := i; Break; end; end; end; end; function FindUserIDByIndex(iIndex: Integer): PVirtualNode; begin Result := _VST.GetFirst(True); while Assigned(Result) and (_VST.AbsoluteIndex(Result) <> Cardinal(iIndex)) do Result := _VST.GetNext(Result, True); end; procedure DeleteUserNameNode(const UserName: string); var Node: PVirtualNode; begin DeleteUserNameNode(FindUserIDByName(UserName)); end; procedure DeleteUserNameNode(iIndex: Integer); var Node: PVirtualNode; begin Node := FindUserNameByIndex(iIndex); if Assigned(Node) then _VST.DeleteNode(Node); end; Bei FindUserIDByName(<string>) halte ich mich ganz klar daran den Nutzer anhand des Namens zu finden und durchsuche die Klasseninstanzen. Bei FindUserNameByIndex(<int>) durchsuche ich aber direkt das Index der VST und greife nicht auf die Klasseninstanzen zu. Da ist doch schon der erste Fehler oder? |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Warum so kompliziert?
Bei allen wichtigen Eventhandlern des VST hast du doch eine Referenz auf den Node. Die Objektliste machste du nur aus Speicherfreigabegründen.
Delphi-Quellcode:
procedure TForm2.VirtualStringTree1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
TextType: TVSTTextType; var CellText: string); var UserData: TUserData; begin if Assigned(Node) then begin UserData := Node.GetData<TUserData>; if Assigned(UserData) then begin case Column of 0: CellText := UserData.FirstName; 1: CellText := UserData.LastName; 2: CellText := UserData.Address; end; end; end; end; |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Wie ich Text lade ist mir klar.
Was mir nicht klar ist, ist der letzte Abschnitt aus meinem ersten Beitrag. Ich möchte, dass wenn ich DeleteUserNameNode(<string>) aufrufe genau dasselbe gelöscht wird wie wenn ich DeleteUserNameNode(<int>) aufrufe. Letzteres ist aber sicher nicht gleich wie ersteres denn in FindUserIDByIndex greife ich direkt auf das VST zu und nicht wie bei allen anderen Prozeduren auf die Klasseninstanzen um an die Daten zu kommen. Ich möchte mir nur ein paar kleine Helfer-prozeduren zusammenstellen, um später doppelten Code zu vermeiden. Aber diese Basis muss auch korrekt sein, sonst geht später alles schief. --------------- Schnipps ich glaube ich habs. Hier meine Änderung. Nodes werden jetzt auch korrekt bei umsortierter Liste gelöscht. Hier ein Beispiel, um Nodes (+ Daten da OwnObjects True) aus einem VST, egal wie es sortiert ist, zu löschen.
Delphi-Quellcode:
Ob das mit dem Index nun so korrekt ist, muss ich erst noch sehen. Aber es erscheint mir logischer über die Variable Index der Klassendaten zu prüfen statt über den VST AbsoluteIndex.
// Helferfunktion, um UserName zu finden
function FindUserIDByName(const UserName: string): Integer; var i: Integer; begin Result := -1; if Assigned(_UserDataClasses) then begin for i := 0 to _UserDataClasses.Count - 1 do begin if AnsiSameText(_UserDataClasses[i].UserName, UserName) then begin Result := i; Break; end; end; end; end; // Helferfunktion, um den Node im VST anhand eines gesuchten Index zurückzugeben (Achtung: nicht AbsoluteIndex, sondern der Index in meinen Klassendaten!) function FindUserIDByIndex(iIndex: Integer): PVirtualNode; var i: Integer; begin Result := nil; for i := 0 to _UserDataClasses.Count - 1 do if _UserDataClasses[i].Index = iIndex then begin Result := _UserDataClasses[i].aNode; Break; end; // Result := _VST.GetFirst(True); // while Assigned(Result) and (_VST.AbsoluteIndex(Result) <> Cardinal(iIndex)) do // Result := _VST.GetNext(Result, True); end; procedure DeleteUserNameNode(const UserName: string); var Node: PVirtualNode; begin DeleteUserNameNode(FindUserIDByName(UserName)); end; procedure DeleteUserNameNode(iIndex: Integer); var Node: PVirtualNode; begin Node := FindUserNameByIndex(iIndex); if Assigned(Node) then _VST.DeleteNode(Node); end; // Aufruf DeleteUserNameNode('Peter Wurst'); // Sucht den Namen, den Index (in den Klassendaten) und löscht den Node DeleteUserNameNode(2); // Sucht in den Klassendaten nach einem Eintrag mit Index = 2 und löscht den dazugehörigen Node |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
In TUserData eine Referenz auf PVirtualNode zu halten ist schon von vornherein ein Designfehler.
Der VST ist nur ein Abbild von deinen Userdaten in _UserDataClasses. Alle Operationen wie hinzufügen und löschen sollten darüber geschehen und danach dann ein aktualisieren/updaten/neuzeichnen des VST. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Delphi-Quellcode:
Danke für den Hinweis. Wie lade ich im obigen Beispiel denn dann einen Node neu, nachdem ich ihn geändert habe? Ohne InvalidateNode müsste ich erst die Maus über das VST bewegen, damit es neugezeichnet wird. Oder zeichnet man einfach das ganze VST mit _VST.Invalidate; neu?
iUserNameID := FindUserNameIDByName('Peter Wurst'); // // Helferfunktion, um den Index der Klasseninstanz für den Nutzer zu finden (um doppelten Code zu vermeiden)
if iUserNameID > -1 then begin _UserDataClasses[iUserNameID].sUserName := 'Peter Lustig'; _VST.InvalidateNode(_UserDataClasses[iUserNameID ].aNode); // ? end; Und das hier funktioniert dann ja auch nicht mehr
Delphi-Quellcode:
function FindUserIDByIndex(iIndex: Integer): PVirtualNode;
var i: Integer; begin Result := nil; for i := 0 to _UserDataClasses.Count - 1 do if _UserDataClasses[i].Index = iIndex then begin Result := _UserDataClasses[i].aNode; // <= Break; end; // Result := _VST.GetFirst(True); // while Assigned(Result) and (_VST.AbsoluteIndex(Result) <> Cardinal(iIndex)) do // Result := _VST.GetNext(Result, True); end; |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Das wird jetzt ineffizient in der Kommunikation.
Vorschlag: Baue ein kleines Beispielprojekt mit den wesentlichen Operationen (hinzufügen, löschen, ändern etc.) und lade es hier als Zip-Archiv hoch. Anhand von richtigen Code kann dir besser geholfen werden. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Das ist nicht so einfach. Aber ich sehe schon, das ich die Referenz auf Node gar nicht weglassen kann.
Wenn ich einen Eintrag aus meiner ObjectList lösche, werden zwar die Daten gelöscht, aber nicht der dazugehörige "Node" selber im VST. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Zitat:
Nach aktualisieren bzw. ändern der Objekte in der Objektliste machst du einfach ein VST.Clear und neu hinzufügen. So hast du immer einen konsistenten Stand. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Wenn ich VST.Clear aufrufe, lösche ich aber auch alle meine Klasseninstanzen.
Das war ja genau das was ich wollte: lösche ich einen Node, löscht er die Klasseninstanz die dranhängt direkt mit. Immer das ganze VST löschen und neuladen war auch genau das, was ich vermeiden wollte mit den Daten die ich im Hintergrund halte. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Warum löscht er deine Instanz? Das kann nur der Fall sein, wenn du OnFreeNode implementiert hast. Das solltest du nicht machen wenn deine Instanzen von der ObjectList verwaltet werden.
EDIT: Du musst natürlich auch aufpassen, dass deine Instanz so lange existiert, wie es eine Node im VST gibt die darauf zugreift. Ansonsten hagelt es AVs. Das bedeutet im Umkehrschluss: Erst die Nodes im VST löschen, dann die Instanz aus der ObjectList werfen und die Nodes wieder erzeugen. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Liste der Anhänge anzeigen (Anzahl: 1)
Ich mache gerade ein Beispiel. 10 Minuten noch.
So ganz klappt es noch nicht. Speziell wenn man erst den Button Node mit Idx 2 löschen drückt und dann Node Peter Wurst löschen. Mein Ziel wäre es, die Referenz auf Node wegzubekommen und die Möglichkeit zu haben, das wenn ich eine Klasseninstanz lösche, auch gleich der Node mit weg ist. Aktuell ist es ja so, dass ich den Node löschen muss. Und das geht nur mit einer Referenz da drauf. Aber ihr seht, mein Code ist wie Pasta ... nix funktioniert. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Das nächste Mal bitte als Projekt ;)
Mach mal folgendes, aber bedenke, dass das jetzt nur eine Lösung für deine Vorgehensweise hier ist. Und bitte alles beachten. Ich hänge bewusst die Datei nicht an, damit du die Änderungen auch selbst einbauen musst und hoffentlich auch verstehst:
Delphi-Quellcode:
procedure TForm2.Button2Click(Sender: TObject);
begin VST.Clear; DeleteNode(2); // Daten anhand des Index löschen. 2 ist nicht der absolute Index sondern der, der in Button1Click gesetzt wird VST.RootNodeCount := aVSTDataClasses.Count; end; procedure TForm2.Button3Click(Sender: TObject); begin VST.Clear; DeleteNode('Peter Wurst'); // Daten anhand eines Nutzernamens löschen VST.RootNodeCount := aVSTDataClasses.Count; end;
Delphi-Quellcode:
procedure DeleteNode(iIndex: Integer); overload;
//var // Node: PVirtualNode; begin aVSTDataClasses.Delete(iIndex); // Node := FindUserNodeByIndex(iIndex); // if Assigned(Node) then // Form2.VST.DeleteNode(Node); end;
Delphi-Quellcode:
procedure TForm2.FormCreate(Sender: TObject);
var Column: TVirtualTreeColumn; i: Byte; begin ReportMemoryLeaksOnShutdown := True; ... VST.OnInitNode := VSTInitNode; // VST.OnFreeNode := VSTFreeNode; // <--- WICHTIG! ENTFERNEN! VST.OnGetText := VSTGetText; end; |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Ok Moment ganz langsam für meinen alten Kopf.
Heißt das jetzt, dass du mit 2, 3 Zeilen Umstellung das so gemacht hast, dass man jetzt eine Klasseninstanz löscht, das Node verschwindet (man löscht ja alle mit Clear) und alles neugezeichnet wird? Wird das Neuzeichnen durch das Setzen von RootNodeCount ausgelöst? Das ist dann ja genau das was TiGü meinte, nur habe ich es leider nicht ganz verstanden. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Ja das ist genau das was TiGü meinte. :thumb:
Dein größter Fehler war eben (wie ich in meinem vorletzten Post schrieb), dass du das OnFreeNode Event implementiert hast. OnFreeNode gibt die Daten die an die Node gebunden sind frei. Zumindest, wenn du das in dem Event so festgelegt hast. Und da du im OnFreeNode() die Instanz per Remove() aus der ObjectList gelöscht hast, wurde die natürlich auch beim leeren des Trees mit VST.Clear() ebenfalls gelöscht. Das Neuzeichnen des Trees wird durch das setzen von RootNodeCount, aber auch durch das vorhergehende löschen der Nodes mit VST.Clear() ausgeführt. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
TiGü schriebe, dass es schlechtes Design wäre sich eine Referenz auf Node zu halten.
Ich habe eine Funktion die sieht so aus
Delphi-Quellcode:
Ist von sowas dann abzuraten?
function FindUserNodeByIndex(iIndex: Integer): PVirtualNode;
var i: Integer; begin Result := nil; for i := 0 to aVSTDataClasses.Count - 1 do if aVSTDataClasses[i].Index = iIndex then begin Result := aVSTDataClasses[i].aNode; Break; end; end; Die markierte Zeile unten kann dann vermutlich auch weg
Delphi-Quellcode:
So würde ich dann beispielsweise den Text eines Properties ändern ohne die Node-Referenz
procedure TForm2.VSTInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
begin Node.SetData<TVSTData>(aVSTDataClasses[Node.Index]); aVSTDataClasses[Node.Index].aNode := Node; // ---------------------------------------------- end;
Delphi-Quellcode:
aVSTDataClasses[5].sUserName := 'XXXXXXXXXXXXXX';
VST.Invalidate; // InvalidateNode(aVSTDataClasses[5].aNode); |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Habe dein Edit leider erst nach meinem Post gesehen.
Zitat:
Genau so ist das auch umgekehrt. Wenn du die Daten aktualisiert, dann reicht ein VST.Invalidate(). Wenn Daten hinzugefügt oder gelöscht werden, dann solltest du die Daten auch im VST aktualisieren - sprich löschen und neu erstellen. Das ist das, was TiGü mit konsistentem Zustand meinte. Deine Daten im VST passen immer 100% zu den Daten in deiner ObjectList. EDIT: Oh je. Jetzt ist es kein Edit mehr :D |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Zitat:
Ich frage nur in die Runde. Zitat:
Mit erstellen meinst du ja RootNodeCount setzen denke ich doch? Zitat:
|
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Wie ich sehe, nimmt das langsam vernünftige Formen an.
Höre auf das, was Aviator sagt. Er ist sehr erfahren mit dem VST. Alternativ zum RootNodeCount und OnInitNode-Eventhandler kannst du natürlich auch versuchen mit AddChild zu arbeiten.
Delphi-Quellcode:
Kommt auf das gleiche hinaus, aber vielleicht verstehst du das in zwei Monaten noch, wenn du dir den Code nochmal anguckst.// PseudoCode for deinTyp in deinerListe do begin vst.AddChild(nil, deinTyp); end; |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Zitat:
Zitat:
Zitat:
Zitat:
Er ist mit RootNodeCount schon auf dem richtigen Weg. AddChild() ist zwar anfängerfreundlicher, aber wenn er sowieso schon richtig angefangen hat, dann sollten wir ihn auf keinen Fall auf die falsche Spur locken. Anbei mal noch ein Auszug aus dem Source der VirtualTreeView Komponente (Hervorhebung von mir): Zitat:
|
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Delphi-Quellcode:
Ich höre auf euch beide :thumb:
Höre auf das, was Aviator sagt. Er ist sehr erfahren mit dem VST.
Eine Sache habe ich eben noch hinzugefügt aus
Delphi-Quellcode:
wird
procedure TForm2.Button2Click(Sender: TObject);
begin VST.Clear; DeleteNode(2); // Daten anhand des Index löschen. 2 ist nicht der absolute Index sondern der, der in Button1Click gesetzt wird VST.RootNodeCount := aVSTDataClasses.Count; end;
Delphi-Quellcode:
procedure DeleteUserNode(iIndex: Integer);
begin if iIndex > aVSTDataClasses.Count - 1 then // ---------------- neu Exit; VST.Clear; aVSTDataClasses.Delete(iIndex); VST.RootNodeCount := aVSTDataClasses.Count; // re-draws alle nodes of VST end; procedure TForm2.Button2Click(Sender: TObject); begin DeleteNode(2); end; Zitat:
Zitat:
Mein erstes Erlebnis waren Nodes die nicht richtig gezeichnet wurden, wenn man das AfterItemPaint-Event nutzt. Aber das war nur minimal und nicht weiter schlimm und auch sofort wieder weg, sobald man mit der Maus über den Node gefahren ist. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Zitat:
Delphi-Quellcode:
könntest du es auch so machen. Sieht eventuell besser aus:
Exit
Delphi-Quellcode:
procedure DeleteUserNode(iIndex: Integer);
begin if (iIndex > -1) and (iIndex < aVSTDataClasses.Count) then begin VST.Clear; aVSTDataClasses.Delete(iIndex); VST.RootNodeCount := aVSTDataClasses.Count; // re-draws all nodes of VST end; end; Zitat:
Anbei mal die Reihenfolge der Events (Auszug aus der Hilde des VST): Zitat:
|
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Ich lasse mir zusätzlich zum normalen Node-Text noch eine weitere Zeichenkette ausgeben. Das erledige ich in AfterItemPaint mit TargetCanvas und DrawTextW.
|
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
Zitat:
Oder eben das DrawText Event. Da bist du vollkommen frei und kannst alles selbst malen was dir in den Sinn kommt. Hauptsache es handelt sich um Text und nicht um irgendwelche Cell spezifischen Dinge. AfterItemPaint ist dafür eigentlich ungünstig. EDIT: Vorteil von ttNormal und ttStatic ist, dass der Tree automatisch den Text mit "..." gekürzt abschneidet wenn er nicht passen sollte. Das musst du sonst selbst machen wenn es optisch nach etwas aussehen sollte. Oder du benutzt die Option ColumnSpan, dann zeichnet er den Text in die nächste Column sofern da kein Text steht. |
AW: VirtualStringTree und Klasseninstanzen - Node Data finden
:idea: Für alle die sich wundern wieso hier aktuell nichts mehr passiert: Die Diskussion ging vorerst mal per PM weiter, da im gezeigten SourceCode evtl. sensible Daten enthalten sein könnten.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:38 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