J'ajoute quelques milliers (par exemple 53 709) d'éléments à une liste WinForms.
Tentative 1 : 13,870 ms
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
listView.Items.Add(item);
}
Cela fonctionne très mal. La première solution évidente est d'appeler BeginUpdate/EndUpdate
.
Tentative 2 : 3,106 ms
listView.BeginUpdate();
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
listView.Items.Add(item);
}
listView.EndUpdate();
C'est mieux, mais toujours un ordre de grandeur trop lent. Séparons la création de ListViewItems de l'ajout de ListViewItems, afin de trouver le vrai coupable :
Tentative 3 : 2,631 ms
var items = new List<ListViewItem>();
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
items.Add(item);
}
stopwatch.Start();
listView.BeginUpdate();
foreach (ListViewItem item in items)
listView.Items.Add(item));
listView.EndUpdate();
stopwatch.Stop()
Le véritable goulot d'étranglement est l'ajout des articles. Essayons de le convertir en AddRange
plutôt qu'un foreach
Tentative 4 : 2,182 ms
listView.BeginUpdate();
listView.Items.AddRange(items.ToArray());
listView.EndUpdate();
Un peu mieux. Soyons sûrs que le goulot d'étranglement n'est pas dans le ToArray()
Tentative 5 : 2,132 ms
ListViewItem[] arr = items.ToArray();
stopwatch.Start();
listView.BeginUpdate();
listView.Items.AddRange(arr);
listView.EndUpdate();
stopwatch.Stop();
La limitation semble être l'ajout d'éléments dans le listview. Peut-être que l'autre surcharge de AddRange
où nous ajoutons un ListView.ListViewItemCollection
plutôt qu'un tableau
Tentative 6 : 2,141 ms
listView.BeginUpdate();
ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView);
lvic.AddRange(arr);
listView.EndUpdate();
Ce n'est pas mieux.
Maintenant, il est temps de s'étirer :
-
Étape 1 - assurez-vous qu'aucune colonne n'est définie comme "auto-width" :
Vérifiez
-
Étape 2 - Assurez-vous que le ListView n'essaie pas de trier les éléments à chaque fois que j'en ajoute un :
Vérifiez
-
Étape 3 - Demandez à stackoverflow :
Vérifiez
Nota: De toute évidence, cette liste n'est pas en mode virtuel ; puisque vous ne pouvez pas "ajouter" d'éléments à une liste virtuelle (vous définissez l'attribut VirtualListSize
). Heureusement, ma question ne porte pas sur une vue de liste en mode virtuel.
Y a-t-il quelque chose qui m'échappe et qui pourrait expliquer que l'ajout d'éléments à la liste soit si lent ?
Bonus Chatter
je sais que la classe ListView de Windows peut faire mieux, car je peux écrire du code qui le fait en 394 ms
:
ListView1.Items.BeginUpdate;
for i := 1 to 53709 do
ListView1.Items.Add();
ListView1.Items.EndUpdate;
qui, lorsqu'il est comparé au code C# équivalent 1,349 ms
:
listView.BeginUpdate();
for (int i = 1; i <= 53709; i++)
listView.Items.Add(new ListViewItem());
listView.EndUpdate();
est un ordre de grandeur plus rapide.
Quelle propriété du wrapper ListView de WinForms me manque-t-il ?
2 votes
Remarque : si vous utilisez des cases à cocher, vous devez définir l'état coché avant de les ajouter à la liste. Initialisation des états cochés blogs.msdn.com/b/hippietim/archive/2006/03/20/556007.aspx
3 votes
Je dois demander : pourquoi ajoutez-vous autant d'articles ?
4 votes
Bonne question Ian. Avez-vous vu ce blog sur le sujet ? virtualdub.org/blog/pivot/entry.php?id=273
0 votes
Avez-vous essayé un for au lieu d'un foreach ?
0 votes
@subt13 Parce que le tirage au sort promotionnel a, dans cette promotion, 53 709 joueurs de haut niveau invités. S'il n'y avait que 10 000 personnes invitées, alors la liste ne comporterait que 10 000 éléments.
2 votes
1,349 ms, impossible. J'essaie avec 53709 éléments, cela prend quelques minutes. Pourquoi utiliser une vue en liste avec autant d'éléments ? Ce n'est pas vraiment utilisable. Vous pouvez utiliser listBox ou comboBox pour augmenter la vitesse mais c'est un nombre insensé.
2 votes
@IanBoyd - Pour quelle raison avez-vous besoin d'afficher autant d'éléments, c'est ma prochaine question. Pourquoi ne pas filtrer la liste à un nombre raisonnable ? C'est pourquoi les grilles utilisent des pagers :).
0 votes
@ChrisShain C'est un article de blog très intéressant. Je devrais probablement essayer de remplacer le ListView WinForms pour restaurer le comportement par défaut de Windows.
1 votes
@subt13 L'utilisateur peut donc utiliser le tri par clic et le filtrage par recherche instantanée pour trouver les éléments qu'il souhaite. Je ne veux pas limiter l'expérience de l'utilisateur à cause des limitations d'un contrôle WinForms. Regardez Picasa, des centaines de milliers d'images toutes disponibles instantanément.
1 votes
@IanBoyd - Je ne veux pas polémiquer, mais qui sait comment Picasa s'y prend ? Vous n'avez toujours pas besoin d'alimenter la liste entière pour faire un tri par clic et un filtrage de recherche instantané. Pas du tout. Je pense que votre approche de la question est tout à fait erronée.
0 votes
Essayez de supprimer toutes les colonnes et d'ajouter des éléments sans aucun texte. Est-ce plus rapide ?
0 votes
@subt13 Je disais juste que montrer tous les éléments à l'utilisateur est correct et bon. Je sais que le contrôle ListView de Windows peut gérer une telle chose, car il le fait déjà. Ce dont j'ai besoin, c'est de savoir comment le faire en .NET.
0 votes
@Xilmiki Êtes-vous en train de dire que
1,349 ms
semble trop élevé, et il pourrait y avoir une amélioration des performances que je manque ? Ou êtes-vous en train de dire que vous ne croyez pas que j'ai été capable d'ajouter 50 000 éléments aussi rapidement que 1,3 seconde ?4 votes
Pourquoi ne pas utiliser Virtual List View ? D'accord, vous devez programmer la façon de récupérer les données des éléments et vous pourriez avoir à gérer d'autres choses comme le tri, le filtrage, etc. mais vous remplirez la vue de liste instantanément, quel que soit le nombre d'éléments.
0 votes
@Casperah J'ai commencé. Mais il faudra environ 3 jours de travail pour réimplémenter tout ce que le ListView fait déjà (mais cette fois plus rapidement).
0 votes
Avez-vous essayé de lier votre ensemble de données au contrôle au lieu d'itérer et d'ajouter à chaque fois ?
0 votes
Hmm oui, l'implémentation WinForms du contrôle ListView est connue pour être lente et problématique à d'autres égards. Le code pour l'ajout d'éléments est à peine optimisé, et il y a beaucoup de choses qui se passent dans les coulisses. En général, si vous ressentez le besoin de vitesse, vous n'écrirez pas de code géré. Les alternatives sont soit d'utiliser le mode virtuel (probablement un meilleur choix de toute façon si vous affichez plus de 10.000 éléments...), soit d'écrire votre propre enveloppe autour du contrôle ListView Win32 sans toute la surcharge supplémentaire. Cela représente probablement plus de travail qu'il n'en vaut la peine, et il se peut que le problème ne soit toujours pas résolu.
0 votes
La seule autre chose à vérifier (bien que j'hésite, car j'ai l'impression de vous insulter en suggérant cela, même si ce n'est pas mon intention) est que vous sont exécuter ces tests avec les optimisations activées (mode "Release"). Il y a beaucoup de extra La vérification des limites en mode débogage va ralentir encore plus les choses.
1 votes
@CodyGray Il est trop tard pour moi pour vérifier l'ancienne méthode maintenant. Je suis maintenant en train de recréer un listview à partir d'un listview virtuel. C'est vachement plus rapide, avec toutes les mêmes fonctionnalités (c'est vraiment triste que l'équipe .NET se soit trompée à ce point).