Autor Beitrag
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 04.07.13 22:39 
Hallo,

da immer wieder mal die Frage kommt wie man die TVirtualStringTree nutzen kann, möchte ich das an einem kleinen Beispiel demonstrieren.
Vorneweg: Das funktioniert so nur mit Delphi 2009 oder höher.

Hintergrund:
In den Knoten können beliebige Datenstrukturen abgelegt werden, solange man deren Größe angibt (NodeDataSize). Am besten funktioniert das, wenn man im Hintergrund seine Daten irgendwo hat und dann nur eine Referenz darauf in den Node packt. So ist die Datenhaltung von der Oberfläche getrennt, aber dennoch kommt man an die Daten zu den Knoten und kann damit arbeiten.

Konkret:
Zur Datenspeicherung benutze ich im Beispiel einfach eine generische Liste, in der Objekte liegen. Um die Freigabe kümmert sich die Liste selbst. Um die Trennung der einzelnen Teile zu simulieren verwende ich verschiedene Methoden (LoadData zum Laden der Daten und DisplayData zur Anzeige), im echten Programm sollten das natürlich alles eigene Klassen und Units sein.

Die Initialisierung sieht so aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
  FData := TObjectList<TExampleNode>.Create(True); // Datenstruktur
  VirtualStringTree1.NodeDataSize := SizeOf(TExampleNode); // Größe dem virtuellen Tree mitteilen
  LoadData; // Daten laden
  DisplayData; // Daten anzeigen

Beim Laden werden die Objekte erzeugt und in der Liste hinzugefügt:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TfrmExample.LoadData;
var
  i: Integer;
begin
  for i := 1 to 10 do
    FData.Add(TExampleNode.Create('Example ' + IntToStr(i)));
end;

Und zur Anzeige werden die Referenzen auf diese Objekte als Benutzerdaten bei der Erzeugung der Nodes angegeben:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TfrmExample.DisplayData;
var
  i: Integer;
begin
  for i := 0 to FData.Count - 1 do
    VirtualStringTree1.AddChild(nil, FData[i]);
end;

Nun zum auslesen. Hier kommen wir zu dem Teil, der erst ab Delphi 2009 funktioniert. Vorher kann man das natürlich ähnlich machen, nur nicht so schön gekapselt. In OnGetText muss man dem Baum mitteilen welcher Text zu einem bestimmten Node angezeigt werden soll. Das heißt ich greife auf die Daten zu und hole mir aus meinem Objekt die Daten, in unserem Fall einen einfachen String.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure TfrmExample.VirtualStringTree1GetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: string);
begin
  CellText := Sender.Get<TExampleNode>(Node).Example;
end;

Damit das so einfach funktioniert, wird ein Class Helper gebraucht, sprich eine Erweiterung zur TBaseVirtualTree, die diese generische Methode Get anbietet:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
type
  TTypedVirtualStringTreeHelper = class helper for TBaseVirtualTree
    function Get<T>(const ANode: PVirtualNode): T;
  end;

function TTypedVirtualStringTreeHelper.Get<T>(const ANode: PVirtualNode): T;
var
  DataPointer: Pointer;
begin
  DataPointer := GetNodeData(ANode);
  if Assigned(DataPointer) then
    Result := T(DataPointer^);
end;

Darin wird der Pointer zu den Daten geholt und dereferenziert. Da ich den Typ T in der generischen Methode angebe, kann ich auch auf den Typ casten. Nach außen wird also nur dieser Typ benutzt.

Natürlich kann man auch eine Methode schreiben, die das umgekehrt macht, oder gleich eine eigene generische TVirtualStringTree ableiten. Aber da AddChild ohnehin gleich direkt das Objekt bekommen kann, halte ich das nicht für notwendig. Man muss natürlich darauf achten, dass man auch wirklich den richtigen Typ in den Baum packt.

Wenn man mehrere Typen drin hat, kann man eine Hilfsmethode schreiben, die intern auf TObject castet und dann den Typ prüft und dann eben nur das Objekt liefert, wenn es auch der richtige Typ ist.

Ein Beispielprojekt (erstellt mit XE4) findet ihr im Anhang.

Viel Erfolg mit den Informationen,
Schöne Grüße,
Sebastian Jänicke
Einloggen, um Attachments anzusehen!

Für diesen Beitrag haben gedankt: NOS1971