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
TImageList
sera stocké dans la propertyimages
et la largeur définie dans la propetyimagesWidth
. - L'image “State”, dont le
TImageList
sera stocké dans la propertyStateImages
et 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
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;
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;