2010-02-06 9 views
3

de Delphi [Ceci est une version mise à jour d'une question posée plus tôt, le titre précédent était noeud Sélection par index dans Treeview virtuel de Delphi.]défilement rapide dans Virtual Treeview

Après la meilleure partie d'un jour , Je crois que j'ai le composant Virtual Treeview (puissant mais complexe) fonctionnant d'une manière simple de deux données de table. Maintenant, j'essaie simplement de sélectionner le 1 512e (par exemple) des nœuds de niveau supérieur. Je ne vois pas d'autre moyen de le faire que d'obtenir le premier nœud de niveau supérieur et d'appeler ensuite GetNextSibling 1 511 en boucle.

Cela semble inutilement impliqué. Y a-t-il un moyen plus simple?

MISE À JOUR

Parce que l'initialisation des noeuds dans mon arbre nécessite un accès de base de données, l'initialisation tous les noeuds au démarrage est impossible. Lorsque l'utilisateur démarre avec un formulaire sans enregistrement déjà sélectionné, c'est bien. Lorsque l'utilisateur parcourt l'arborescence, suffisamment de nœuds sont remplis pour afficher la fenêtre en cours dans l'arborescence et les performances sont bonnes. Lorsque l'utilisateur démarre le formulaire en mode de dialogue avec un enregistrement de base de données déjà sélectionné, je dois avancer l'arborescence vers ce nœud avant que l'utilisateur ne voit le formulaire. C'est un problème parce que, si l'enregistrement est vers la fin de l'arbre, cela peut prendre dix secondes pendant que je marche l'arbre du premier noeud. Chaque fois que je peux GetNextSibling(), un nœud est initialisé, même si la grande majorité de ces nœuds ne sont pas affichés à l'utilisateur. Je préférerais différer l'initialisation de ces nœuds au point où ils deviennent visibles pour l'utilisateur.

Je sais qu'il doit y avoir un meilleur moyen, car si j'ouvre l'arbre sans un enregistrement sélectionné et que j'utilise la barre de défilement verticale pour me déplacer en une seule opération au milieu de l'arbre, les nœuds corrects sont affichés sans avoir à initialiser les nœuds que j'ai ignorés.

C'est l'effet que j'aimerais obtenir en ouvrant l'arborescence avec un enregistrement sélectionné. Je connais l'index du noeud que je veux aller, mais si je ne peux pas y arriver par index, je pourrais faire une recherche binaire sur l'arbre en supposant que je peux sauter un certain nombre de nœuds en arrière et en avant (similaire au milieu de l'arbre).

Alternativement, peut-être il y a un certain paramètre d'état que je peux faire à l'arborescence qui laissera les nœuds intermédiaires non initialisés pendant que je traverse la grille. J'ai essayé Begin/End Update et cela ne semble pas faire l'affaire.

Répondre

3

Pour obtenir le frère d'un nœud sans l'initialiser, il suffit d'utiliser le pointeur NextSibling (voir la déclaration de TVirtualNode).

+0

Fonctionne parfaitement, avec un temps apparent nul pour parcourir plus de 9 000 noeuds. –

3

Le contrôle des arbres est structuré comme les arbres classiques que vous apprendriez dans un cours d'informatique. La seule façon d'aller de la racine de l'arbre au 1512ème enfant est de marcher les liens un par un. Que vous le fassiez vous-même ou que vous utilisiez une méthode de contrôle des arbres, cela doit encore être fait de cette façon. Je ne vois rien prévu dans le contrôle lui-même, pour que vous puissiez utiliser cette fonction:

function GetNthNextSibling(Node: PVirtualNode; N: Cardinal; 
    Tree: TBaseVirtualTree = nil): PVirtualNode; 
begin 
    if not Assigned(Tree) then 
    Tree := TreeFromNode(Node); 
    Result := Node; 
    while Assigned(Result) and (N > 0) do begin 
    Dec(N); 
    Result := Tree.GetNextSibling(Result); 
    end; 
end; 

Si vous vous trouvez souvent le faire, vous pouvez vous faire un index. Cela peut être aussi simple que de créer un tableau de pointeurs PVirtualNode et de stocker toutes les valeurs de niveau supérieur, vous pouvez donc en lire la 1512e valeur. Le contrôle d'arbre n'a pas besoin d'une telle structure de données elle-même, donc il n'en maintient pas.

Vous pouvez également reconsidérer si besoin d'une structure de données comme ça. Avez-vous vraiment besoin d'accéder aux nœuds par index comme ça? Ou pourrait à la place maintenir un pointeur PVirtualNode, donc sa position par rapport au reste des nœuds dans l'arbre n'a plus d'importance (ce qui signifie que vous pouvez, par exemple, les trier sans perdre la référence au nœud que vous vouliez)?

+0

J'ai besoin d'accéder à l'arbre par index. C'est une boîte de dialogue pour sélectionner un enregistrement d'une paire d'ensembles de données connexes. Si la boîte de dialogue s'ouvre dans le cas où une valeur est déjà sélectionnée, je veux que la boîte de dialogue affiche le nœud actuellement sélectionné. Je trouve cet enregistrement en faisant un Locate sur l'ensemble de données, le numéro d'enregistrement me donne l'index du noeud que je suis après. Je pourrais construire mon propre index comme vous le suggérez, mais je m'attendais à le trouver intégré dans le composant (je souffre d'une ancienne pensée, venant directement du paradigme Delphis TTreeView.Nodes). –

0

Vous écrivez dans votre mise à jour:

Je sais qu'il doit y avoir une meilleure façon, parce que si j'ouvre l'arbre sans enregistrement sélectionné et utiliser la barre de défilement verticale pour se déplacer, en une seule opération, Au milieu de l'arbre, les noeuds corrects sont affichés sans avoir à initialiser les noeuds que j'ai ignorés.

Il y a une différence ici, parce que le défilement vertical modifie le Y logique de coordonnées qui est affiché à la position du client 0. La commande calcule le décalage de la position de la barre de défilement et la plage de défilement, et calcule ensuite le noeud est visible en haut du contrôle. Les nœuds ne sont réinitialisés que lorsque la zone défilée dans la vue doit être peinte.

Si vous avez la coordonnée Y d'un nœud, vous pouvez obtenir le pointeur de noeud en appelant

function TBaseVirtualTree.GetNodeAt(X, Y: Integer; Relative: Boolean; 
    var NodeTop: Integer): PVirtualNode; 

La coordonnée Y d'un nœud est la somme des hauteurs de tous les noeuds visibles précédents. En supposant que vous n'avez pas de nœuds réduits (donc c'est soit une liste plate d'enregistrements, soit tous les nœuds avec des nœuds enfants sont développés) et ils ont tous la hauteur par défaut, c'est facile. Ce code devrait être un bon point de départ:

procedure TForm1.SelectTreeNode(AIndex: integer; ACenterNodeInTree: boolean); 
var 
    Y, Dummy: integer; 
    Node: PVirtualNode; 
begin 
    Y := Round((AIndex + 0.5) * VirtualStringTree1.DefaultNodeHeight); 
    Node := VirtualStringTree1.GetNodeAt(0, Y, False, Dummy); 
    if Node <> nil then begin 
    Assert(Node.Index = AIndex); 
    VirtualStringTree1.ScrollIntoView(Node, ACenterNodeInTree); 
    VirtualStringTree1.Selected[Node] := True; 
    VirtualStringTree1.FocusedNode := Node; 
    end; 
end; 
+0

Merci. Je ne pense pas que ce soit aussi simple car je ne peux pas être sûr de l'état étendu des nœuds intermédiaires, mais je suis sûr que je peux utiliser cette technique comme base d'une recherche incrémentale ou binaire à travers l'arbre. L'élément manquant était l'idée de faire défiler les pixels au lieu des nœuds. –