Home » Tutorials » Third-Party-Komponenten » VirtualTreeView

VirtualTreeView

Mit Spalten arbeiten

Ein weiterer offensichtlicher Unterschied zur normalen TreeView Komponente ist die Möglichkeit, eine Baumstruktur auch mit Hilfe von Spalten anzulegen.

Wähle dazu im Objektinspektor die Eigenschaft Header aus. Dort erstellst du über den PropertyEditor von Columns die verschiedenen Spalten. Im Options-Menü des Header-Eigenschaft musst du die Option hoVisible noch aktivieren.

Zwar können die einzelnen Captions der Spalten auch zur Entwicklungszeit bequem per Objektinspektor gesetzt werden, aber sicherlich ist es irgendwann mal nötig dies auch per Programm zu tun:

procedure TForm1.Button2Click(Sender: TObject);
begin
  VST.Header.Columns[0].Text := 'Erste Spalte';
end;

Dies würde die Caption der ersten Spalte auf den Wert ‚Erste Spalte‘ setzen. Mit Hilfe der Eigenschaft Width kann die Breite der Spalte angegeben werden. Um eine Spalte unsichtbar zu machen reicht folgende Codezeile:

procedure TForm1.Button2Click(Sender: TObject);
begin
  VST.Header.Columns[0].Options := VST.Header.Columns[0].Options - [coVisible];
end;

Die Spalte wird wieder sichtbar, wenn man aus dem – einfach ein + macht.

Standardmäßig verhalten sich die Spalten-Header wie Buttons. Teste es selbst, indem du dein aktuelles Projekt kompilierst und mit der Maus auf die Spalten klickst. Dabei werden übrigens auch zwei Ereignisse ausgelöst: OnColumnClick bzw. OnColumnDblClick. Erstes wird beim einfachen Klick ausgelöst, letzteres bei einem Doppelklick. Der Procedurekopf des Event-Handler kann so aussehen:

procedure TForm1.vstColumnClick(Sender: TBaseVirtualTree; Column: Integer;
 Shift: TShiftState);

Der Parameter Column enthält den Index der Spalte, auf die der Anwender geklickt hat.

Möchte man aus den Headern statische Beschriftungen, wie z.B. beim StringGrid, machen, muss man den Wert coAllowClick aus den Optionen der Header entfernen.

Nodes und Spalten

Nun kann man auch jedem Node noch weitere Spaltenwerte anzeigen lasse. Wir haben dem Record TTreeData im 3. Kapitel ja schon eine weitere, bisher unbenutzte Variable gegönnt.

type
  PTreeData = ^TTreeData;
  TTreeData = record
    FCaption: String;
    FColumn1: String;
  end;

In der Procedure, wo dem Baum die Nodes hinzugefügt werden, müssen wir diese Variable natürlich auch noch mit Werten füllen, die dann auch mit dem Data-Record in der Funktion AddVSTStructure verknüpft werden.

function AddVSTStructure(AVST: TCustomVirtualStringTree; ANode: PVirtualNode;
 ARecord: TTreeData): PVirtualNode;
var
  Data: PTreeData;
begin
  Result := AVST.AddChild(ANode);
  Data := AVST.GetNodeData(Result);
  AVST.ValidateNode(Result, False);
  Data^.FCaption := ARecord.FCaption;
  Data^.FColumn1 := ARecord.FColumn1;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  I: Integer;
  TreeData: TTreeData;
begin
  VST.NodeDataSize:=SizeOf(TTreeData);
  VST.BeginUpdate;
  for I:=0 to 100 do
  begin
    TreeData.FCaption := 'Node-Nummer: '+IntToStr(I);
    TreeData.FColumn1 := 'Nummer: '+IntToStr(I*I);
    AddVSTStructure(VST, nil, TreeData);
  end;
  VST.EndUpdate;
end;

Anmerkung: Diese Procedure kann man direkt aus dem alten Beispiel übernehmen.

Des weiteren muss jetzt noch das OnGetText-Ereignis von vorhin leicht modifiziert werden. Schaut man sich die Parameterliste der Ereignisprocedure genau an, wird man u.a. auch den Parameter ‚Column‘ sehen, der angibt um welche Spalte es sich handelt:

procedure TForm1.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
 Column: Integer; TextType: TVSTTextType; var Text: WideString);
var
  Data: PTreeData;
begin
  Data := VST.GetNodeData(Node);
  case Column of
    0: Text := Data.FCaption;
    1: Text := Data.FColumn1;
  end;
end;

Die Case-Abfrage sollte verständlich sein. Wie bei vielen Elementen beginnt die Zählung auch hier mit 0. Die erste Spalte hat also den Index 0. Wurden keine Spalten definiert (was standardmäßig der Fall ist) hat der Parameter Column den Wert -1. Es sollte aber mindestens eine Spalte definiert sein, da es sonst Probleme mit dem Selektieren der Nodes zur Laufzeit gibt.

Natürlich müssen die Daten auch wieder freigegeben werden. Benutze dazu die Procedure aus Kapitel 3.

Generelle Header-Ereignisse

Auf der Ereignisseite des Objektinspektor befinden sich mehrere Ereignisse, die etwas mit dem Header zu tun haben. Sie beginnen alle mit OnHeader… . Auf diese Ereignisse möchte ich nun kurz eingehen:

OnHeaderClick & OnHeaderDblClick

    Dieses Ereignis wird ausgelöst, wenn der Anwender an einer beliebiger Stelle mit der Maus auf den Header (doppel-)klickt.

OnHeaderDragged & OnHeaderDragging

    Die VirtualTrees bringen von Haus aus mehrere Methoden um Elemente per Drag & Drop zu bewegen mit. Man kann Beispielsweise mit der Maus die Reihenfolge der Header-Spalten ändern. OnHeaderDragging wird vor dem Drag-Vorgang ausgelöst (der dann über den Parameter Allowed auch gleich unterbunden werden kann) und OnHeaderDragging nach der Drop-Aktion. Möchte man die Drag & Drop Funktionalität von vornherein ausschalten, hilft es den Wert hoDrag aus der Eigenschaft Header.Options des Baums zu entfernen.

OnHeaderDraw

    Werden die Header neu gezeichnet, wird zuvor dieses Ereignis ausgelöst.

OnHeaderMouseDown, OnHeaderMouseMove & OnHeaderMouseUp

    Diese drei Ereignisse sind stark verwandt mit den Ereignissen OnMouseDown, OnMouseMove und OnMouseUp, die jede von TControl abgeleitete Komponente besitzt. OnHeaderMouseDown wird ausgelöst, wenn der Benutzer mit einer beliebigen Maustaste auf eine Stelle im Headerbereich klickt, OnHeaderMouseMove wenn er die Maus im Headerbereich bewegt und onHeaderMouseUp, wenn er die Maustaste wieder loslässt.