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;
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.
- L'image “Normale”, dont le
TImageListsera stocké dans la propertyimageset la largeur définie dans la propetyimagesWidth. - L'image “State”, dont le
TImageListsera stocké dans la propertyStateImageset la largeur définie dans la propetyStateImagesWidth.
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éeikState: 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
{ Expand un Node } VSTMain.Expanded[FRootNode]:=true; { Savoir si un Node est expanded } ExpBool:=VSTMain.Expanded[FRootNode];
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/hoHeaderClickAutoSortdoit 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;
Séléction dans un TNodeArray
var vI: integer; vNodes: TNodeArray; begin vNodes := TVirtualStringTree(Source).GetSortedSelection(True); for vI := 0 to length(vNodes) - 1 do ... 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
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;