Ceci est une ancienne révision du document !


Virtual Tree View

Fonctionnement

Les TVirtualTrees contiennent un ensemble de Node (de type PVirtualNode).

Chaque Node contient un membre Data TVirtualNode.Data (Data: record end;)

Ce Data contient la donnée a gérer pour ce nœud.
En general la donnée est un record qui peut contenir n'importe quoi , par exemple…

TTreeData = record
      Text: String;
      URL: string[255];
      ImageIndex: integer;
   end;  
 
PTreeData = ^TTreeData;

On déclare aussi un type pointer vers TTreeData (PTreeData) qui nous sera utile plus tard…

Initialisation

Il faut initialiser le TVirtualTree en lui disant la taille des noeuds grace a NodeDataSize

procedure TForm1.FormCreate(Sender: TObject);
begin
   VSTMain.NodeDataSize := SizeOf(TTreeData);
end;
Notez bien que c'est SizeOf(TTreeData) et NON SizeOf(PTreeData)
Sinon ça bug dans tous les sens…

Ajout d'un Noeud

Pour ajouter un noeud :

procedure TForm1.FormCreate(Sender: TObject);
var
   pNode: PVirtualNode;
   pData: PTreeData;
begin
   VSTMain.BeginUpdate;
 
   pNode := VSTMain.AddChild(nil);
   pData := VSTMain.GetNodeData(pNode);
   pData^.Text := 'root';
 
   VSTMain.EndUpdate;
 
end;

Affichage d'un Noeud

Affichage du texte du noeud

Lors du dessin du composant, le TVirtualTree se sert de l'evenement OnGetText pour recuperer le texte a afficher.

procedure TForm1.VSTMainGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
   Column: TColumnIndex; TextType: TVSTTextType; var CellText: String);
var
   pData: PTreeData;
begin
   pData := VST.GetNodeData(Node);
   case Column of
      0: CellText:= IntToStr(pData^.id);
      1: CellText:= IntToStr(pData^.Flags);
      2: CellText:= IntToStr(pData^.Current);
      3: CellText:= IntToStr(pData^.Worst);
   end;
end;

Affichage des images/icones

On peut mettre deux images avant le texte du noeud.

  1. L'image “Normale”, dont le TImageList sera stocké dans la property images et la largeur définie dans la propety imagesWidth.
  2. L'image “State”, dont le TImageList sera stocké dans la property StateImages et la largeur définie dans la propety StateImagesWidth.

Lorsque le VST veux récupérer l'index de l'image à afficher, le paramètre Kind contient l'information du type d'image qu'il souhaite:

  • ikNormal : L'image “Normale”
  • ikSelected : L'image “Normale” quand la ligne est séléctionnée
  • ikState : L'image “State”
  • ikOverlay : …..

Le paramètre Column contient l'index de la colonne pour laquelle il veut l'ImageIndex.

procedure TFrameVSTDisks.VSTGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: boolean; var ImageIndex: integer);
var
   pData: PTreeData;
begin
   pData      := VST.GetNodeData(Node);
   ImageIndex := -1;
   case Column of
      C_COL_DEVICE: case Kind of
            ikState: ImageIndex  := -1;
            ikNormal,ikSelected: ImageIndex := pData^.Img;
         end;
      C_COL_SMART: case Kind of
            ikState: ImageIndex  := -1;
            ikNormal,ikSelected: ImageIndex := pData^.SmartImg;
         end;
   end;
end;

Expanded

Un noeud

   VSTMain.Expanded[FRootNode]:=true;

Tout

    VST.FullExpand;
    VST.FullCollapse;

Tri

Le tri d'une colonne peut etre déclenché par :

  • Click sur l’entête de la colonne. Dans ce cas l'option Header/Options/hoHeaderClickAutoSort doit etre à True.
  • Appel a la procédure Sort : VST.Sort(Node,Column,Sens);

Pour comparer les nœuds le VST utilise l'event OnCompareNodes

{ Exemple simpliste sans prise en compte de l'index de la column }
procedure TFrameVSTDisks.VSTCompareNodes(Sender: TBaseVirtualTree; Node1,
	   Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
var
   pD1,pD2: PTreeData;
begin
   pD1:= VST.GetNodeData(Node1);
   pD2:= VST.GetNodeData(Node2);
   Result:=0;  // Les noeud sont égaux
   if pD1^.Index>pD2^.Index then Result:=1;   // node1>node2 -> Result=  1
   if pD1^.Index<pD2^.Index then Result:=-1;   // node1<node2 -> Result=  -1
end;

Ressources pour le tri :

Selection

GetFirstSelected

function TForm1.GetSelectedMarker: TMarker;
var
   pNode: PVirtualNode;
   pData: PVSTMarkData;
begin
   pNode := VSTMark.GetFirstSelected;
   if assigned(pNode) then
   begin
      pData  := VSTMark.GetNodeData(pNode);
      Result := pData^.Marker;
   end else
      Result := nil;
end;

Modifications

BeginUpdate / EndUpdate

Lorsque l'on met a jour un VST, il est bon pour l'affichage et la rapidité d'entourer ces mises a jour par un BeginUpdate et un EndUpdate.

   VST.BeginUpdate;
   try
     ...
   finally
     VST.EndUpdate;
   end;       

Parcourir un VST

Parcourir un méme niveau

Version courte
var
   Data: PTreeData;
   Node: PVirtualNode;
begin
      Node := VST.GetFirst;
      while Assigned(Node) do
      begin
         Data := VST.GetNodeData(Node);
        Node := VST.GetNextSibling(Node);
      end;
end;
version détaillée
var
   Data: PTreeData;
   Node: PVirtualNode;
begin
   VST.ClearSelection;
   if assigned(ATpl) then
   begin
      Node := VST.GetFirst;
      while Assigned(Node) do
      begin
         Data := VST.GetNodeData(Node);
         if Data^.Style=AValue then
         begin
            VST.Selected[Node]:=true;
            break;
	end else
	    Node := VST.GetNextSibling(Node);
      end;
   end;
end;

Parcourir tous les nœuds par Itération

Dans cet exemple on déclare un fonction VSTFindParentNode(AUID: TUniqId) qui doit retourner le Node ayant pour ID AUID.
Cette function utilise IterateSubtree du VST pour chercher le noeud.
La function appelé lors de cette itération est VSTIterationForFindParentNode.
Les données utiles pour la recherche et le résultat sont stockées dans un Record PIterationDataForFindParentNode transmis via le paramétre data: pointer de l'itération.

type
   TVSTMarkType = (vmtMarker, vmtGroup);
 
   TVSTMarkData = packed record
      Obj: TObject;
      Typ: TVSTMarkType;
      UID: TUniqID;
   end;
   PVSTMarkData = ^TVSTMarkData;
 
   TIterationDataForFindParentNode = packed record
      UIDToFind: TUniqID;
      Result:    PVirtualNode;
   end;
   PIterationDataForFindParentNode = ^TIterationDataForFindParentNode;
 
........................
 
procedure TFrameMarkersManager.VSTIterationForFindParentNode(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: boolean);
var
   pData: PVSTMarkData;
   pIterationData: PIterationDataForFindParentNode;
begin
   { On récupére les données a chercher, transmise via le pointer Data }
   pIterationData:=PIterationDataForFindParentNode(Data);
 
   pData:=Sender.GetNodeData(Node);
   { Si le Node est un Group et que l'UID est le bon... }
   if (pData^.Typ=vmtGroup) and (pData^.UID=pIterationData^.UIDToFind)
   then begin
      { On stock le résultat dans le Record transmis par le pointer Data }
      pIterationData^.Result:=Node;
      { On arrete la recherche }
      Abort:=true;
  end;
end;
 
function TFrameMarkersManager.VSTFindParentNode(AUID: TUniqId): PVirtualNode;
var
   pIterationData: PIterationDataForFindParentNode;
begin
   new(pIterationData);
   pIterationData^.Result:=nil; // Ici sera stocké le résultat par la procedure d'itération
   pIterationData^.UIDToFind:=AUID; // Le UID a chercher
   VSTMark.IterateSubtree(nil, @VSTIterationForFindParentNode,pIterationData);
   Result:=pIterationData^.Result;
   dispose(pIterationData);
end;

Déstruction du VST

OnFreeNode

J'ai eu un pb de MemoryLeak avec un VST que j'ai résolu de la façon suivante
procedure TForm1.VSTMainFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
   Data: PTreeData;
begin
   Data := VSTMain.GetNodeData(Node);
   Finalize(Data^);
end;

On en parle ici :https://www.remkoweijnen.nl/blog/2010/06/09/memory-leaks-when-using-virtual-treeview-component/

Plus d'infos sur Finalize() : https://www.freepascal.org/docs-html/rtl/system/finalize.html

On voit aussi…

procedure TForm1.VSTMainFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
   Data: PTreeData;
begin
   Data := VSTMain.GetNodeData(Node);
   Data^.Text:='';
   Data^.URL:='';
end;

Sources et Ressources

Vous pourriez laisser un commentaire si vous étiez connecté.