Article Index

Le treeview n'appelle pas OnFreeNode par défaut

Note: Cette section ne s'applique plus depuis la révision f22306586809d97181e77989d03c8680f2471ab0, 14/05/2013 21:48:58

Comme vu à la section précédente, OnFreeNode doit être implémenté. Cependant, il est certain qu'aucun de ces gestionnaires n'est appelé par défaut. En effet, le virtual treeview n'appelle OnFreeNode que s'il considère que c'est nécessaire. Et cette décision n'est prise que s'il a été informé que le noeud a besoin d'une libération via le flag vsInitialUserData dans l'état du noeud (Node.States)

Cette valeur est ajoutée par le virtual treeview que lorsqu'un appel à AddChild ou InsertNode est effectué avec une valeur non nulle pour le paramètre UserData.
Dans un cas classique, ça n'arrive jamais et c'est normal, le treeview étant virtuel, il n'est pas logique de donner les données dès la création des noeuds. Il faut donc "ruser" en ajoutant à la main la valeur vsInitialUserData dans les états du noeud nouvellement créé. On le fait de cette façon:

Include(Node.States, vsInitialUserData);

Ainsi, on est sûr que OnFreeNode sera toujours appelé.

Il faut par contre ajouter cette ligne de code de façon à ce qu'elle soit appelée automatiquement par le virtual treeview à chaque fois qu'il crée un nouveau noeud. Le plus sûr est d'utiliser l'événement OnStructureChange du VirtualTreeView qui nous indique un changement de structure (ajout, déplacement, suppression...) et dans les cas d'ajout de node ou d'enfant de node, on fera les appels nécessaires, à savoir initialisation du record et indication de l'existence de cette initialisation au treeview. Voici le code à placer dans un gestionnaire d'événement pour OnStructureChange :

case Reason of
  crChildAdded:
  begin
    Node := Sender.GetLastChildNoInit(Node);
    if Assigned(Node) then
    begin
      Include(Node.States, vsInitialUserData);
      Initialize(PMyRecord(Sender.GetNodeData(Node))^);
    end;
  end;
  crNodeAdded:
  begin
    Include(Node.States, vsInitialUserData);
    Initialize(PMyRecord(Sender.GetNodeData(Node))^);
  end;
end;

Vous aurez sans doute remarqué l'appel à GetLastChildNoInit dans le cas de crChildAdded. Cet appel est nécessaire car dans ce cas là, Node contient le noeud parent, pas le noeud enfant qui vient d'être ajouté et on ne recevra jamais de notification avec crNodeAdded pour l'enfant.

Sans ces ajustements, il est quasiment assuré que l'utilisation des virtual treeview entraîne des fuites mémoires liées aux chaînes qui ne sont pas correctement libérées.