Arborescence lente en C #

J’ai une application héritée qui est écrite en C # et qui affiche une arborescence très complexe avec 10 à 20 000 éléments.

Dans le passé, j’ai rencontré un problème similaire (mais en C ++) que j’ai résolu avec la fonctionnalité OWNERDATA offerte par l’API Win32.

Existe-t-il un mécanisme similaire en C #?

EDIT: Le plan est d’optimiser le temps de création ainsi que le temps de navigation. La méthode disponible via l’API Win32 est excellente dans ces deux cas, car elle réduit à néant le temps d’initialisation et le nombre de demandes d’éléments est limité à celles visibles à la fois. Joshl: En fait, nous faisons exactement ce que vous suggérez, mais nous avons encore besoin de plus d’efficacité.

Je ne crois pas que .NET TreeView prenne en charge ce que vous voulez, bien que ce type de modèle soit pris en charge par DataGridView .NET (voir la propriété VirtualMode de DataGridView). TreeView vous permettra de dessiner vos propres nœuds mais ne vous laissera pas les renseigner à partir d’un magasin virtuel.

Si possible, vous pouvez envisager l’utilisation d’un DataGridView pour votre application. Si ce n’est pas le cas, la gestion manuelle des nœuds (comme joshl le mentionne plus haut) peut s’avérer utile si vous pouvez résoudre certains problèmes d’actualisation correcte de l’écran lorsque les nœuds sont développés. En dehors de cela, vous pouvez consulter certains fournisseurs tiers, tels que celui-ci (Divelements SandGrid) , susceptibles de prendre en charge le mode de fonctionnement souhaité.

REMARQUE: SandGrid n’est plus pris en charge par Divelements à la fin du mois de juillet 2013.

Une technique d’amélioration des performances consiste à charger des TreeNodes lorsque l’utilisateur développe l’arborescence. Normalement, un utilisateur n’aura pas besoin que 20 000 nœuds soient ouverts à la fois sur son écran. Chargez uniquement le niveau que l’utilisateur a besoin de voir, ainsi que toutes les informations enfants dont vous avez besoin pour afficher correctement les possibilités qui lui sont offertes (icône d’agrandissement s’il existe des enfants, nombres, icons, etc.). Au fur et à mesure que l’utilisateur développe les nœuds, chargez les enfants juste à temps.

Conseil utile de Keith: Avec les winforms TreeView, vous devez avoir au moins un nœud enfant, sinon il n’affiche pas le détail [+], mais vous gérez l’événement TreeNodeExpanded pour supprimer ce nœud factice et remplir les enfants.

Dans notre application WinForm principale, nous avons un arbre chargé en un seul coup:

  • BeginUpdate ()
  • Charge 20.000 nœuds
  • EndUpdate ()

et jusqu’ici la performance est toujours belle. C’est en fait l’un des rares composants que nous ne remplaçons pas par des tiers.

D’après mon expérience, les performances de la fonction TreeView ralentissent lorsque vous chargez des nœuds (en une fois ou à la demande) sans appeler Begin / EndUpdate (), en particulier si vos nœuds sont sortingés, mais si vous appelez correctement Begin / EndUpdate (), vous ne devrait pas vraiment avoir de problèmes de performance liés au composant lui-même.

NOTE: Cette réponse est invalidée par une modification de la part du questionneur qui dit qu’il fait déjà ce genre de chose, mais j’ai décidé de l’afficher quand même pour que d’autres le recherchant à ce sujet le consultent.

Lorsque j’ai fait la même chose dans le passé, j’ai eu tendance à opter pour le style naïf de chargement paresseux.

  • Utilisez la propriété TreeNode.Tag pour contenir une référence que vous pouvez utiliser pour rechercher les enfants.
  • Utilisez l’événement TreeView.BeforeExpand pour renseigner les nœuds enfants.
  • Utilisez éventuellement l’événement TreeView.AfterCollapse pour les supprimer.
  • Pour que les zones [+] / [-] apparaissent, la meilleure méthode que j’ai trouvée est de créer un TreeNode factice singleton qui est ajouté en tant qu’enfant à tous les noeuds non remplis et vous vérifiez son existence avant de BeforeExpand .

Il existe un moyen d’améliorer beaucoup le fonctionnement de TreeView: créer tous les sous-nœuds et les lier entre eux , puis append les nœuds à TreeView. Si c’est la performance graphique dont nous parlons.

 TreeView tree = new TreeView(); TreeNode root = new TreeNode("Root"); PopulateRootNode(root); // Get all your data tree.Nodes.Add(root); 

Sinon, chargez-les noeud par noeud en utilisant OnTreeNodeExpanded .

Pour les données volumineuses dans la programmation Windows C #, que ce soit dans WPF ou WinForms, j’ai traditionnellement ajouté des nœuds de manière dynamic. Je charge la racine de l’arbre initiale + enfants + petits-enfants en profondeur. Lorsqu’un nœud est développé, je charge les nœuds d’arborescence qui représenteraient les petits-enfants du nœud en expansion, le cas échéant.

Ce modèle fonctionne également bien avec la récupération de données. Si vous chargez réellement des données à partir d’une source de milliers ou de millions d’enregistrements, vous ne voudrez probablement pas les charger toutes en amont. Aucun utilisateur ne veut attendre que cela soit chargé, et il n’y a aucune raison de charger des données qui pourraient ne jamais être visualisées.

En règle générale, j’ai chargé les données de noeud des petits-enfants ou des arrière-petits-enfants sur un thread d’arrière-plan, puis je les ai redirigées dans le thread d’interface utilisateur, puis créé et ajouté les nœuds. Cela laisse l’interface utilisateur sensible. Vous pouvez décorer visuellement des nœuds d’arborescence pour indiquer qu’ils sont toujours en cours de chargement dans le cas où un utilisateur devance votre IO vers le magasin de données.