Ich füge ein paar tausend (zB 53.709) Elemente zu einer WinForms ListView hinzu.
Versuch 1 :13,870 ms
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
listView.Items.Add(item);
}
Das läuft sehr schlecht. Die naheliegende erste Lösung besteht darin, anzurufen BeginUpdate/EndUpdate
.
Versuch 2 :3,106 ms
listView.BeginUpdate();
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
listView.Items.Add(item);
}
listView.EndUpdate();
Das ist besser, aber immer noch eine Größenordnung zu langsam. Lassen Sie uns die Erstellung von ListViewItems vom Hinzufügen von ListViewItems trennen, damit wir den tatsächlichen Schuldigen finden:
Versuch 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()
Der eigentliche Engpass ist das Hinzufügen der Elemente. Versuchen wir, es in a AddRange
anstatt in a zu konvertierenforeach
Versuch 4: 2,182 ms
listView.BeginUpdate();
listView.Items.AddRange(items.ToArray());
listView.EndUpdate();
Ein bisschen besser. Stellen wir sicher, dass der Engpass nicht in derToArray()
Versuch 5: 2,132 ms
ListViewItem[] arr = items.ToArray();
stopwatch.Start();
listView.BeginUpdate();
listView.Items.AddRange(arr);
listView.EndUpdate();
stopwatch.Stop();
Die Einschränkung scheint darin zu bestehen, der Listenansicht Elemente hinzuzufügen. Vielleicht die andere Überladung von AddRange
, bei der wir ListView.ListViewItemCollection
eher ein als ein Array hinzufügen
Versuch 6: 2,141 ms
listView.BeginUpdate();
ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView);
lvic.AddRange(arr);
listView.EndUpdate();
Nun, das ist nicht besser.
Jetzt ist es Zeit sich zu strecken:
Schritt 1 - Stellen Sie sicher, dass keine Spalte auf "automatische Breite" eingestellt ist :
Prüfen
Schritt 2 - Stellen Sie sicher, dass ListView nicht jedes Mal, wenn ich eines hinzufüge, versucht, die Elemente zu sortieren:
Prüfen
Schritt 3 - Stackoverflow fragen:
Prüfen
Hinweis: Diese ListView befindet sich offensichtlich nicht im virtuellen Modus. da Sie einer virtuellen Listenansicht keine Elemente hinzufügen können / können (Sie legen die fest VirtualListSize
). Zum Glück geht es bei meiner Frage nicht um eine Listenansicht im virtuellen Modus.
Fehlt mir etwas, das dafür verantwortlich sein könnte, dass das Hinzufügen von Elementen zur Listenansicht so langsam ist?
Bonus Chatter
Ich weiß, dass die Windows ListView-Klasse es besser kann, weil ich Code schreiben kann, der dies tut 394 ms
:
ListView1.Items.BeginUpdate;
for i := 1 to 53709 do
ListView1.Items.Add();
ListView1.Items.EndUpdate;
was im Vergleich zum entsprechenden C # -Code 1,349 ms
:
listView.BeginUpdate();
for (int i = 1; i <= 53709; i++)
listView.Items.Add(new ListViewItem());
listView.EndUpdate();
ist eine Größenordnung schneller.
Welche Eigenschaft des WinForms ListView-Wrappers fehlt mir?
quelle
Antworten:
Ich habe mir den Quellcode für die Listenansicht angesehen und einige Dinge bemerkt, die die Leistung möglicherweise um den Faktor 4 verlangsamen, so dass Sie Folgendes sehen:
ListViewItemsCollection.AddRange
ruft in ListView.cs auf,ListViewNativeItemCollection.AddRange
wo ich meine Prüfung begonnen habeListViewNativeItemCollection.AddRange
(aus Zeile: 18120) hat zwei Durchgänge durch die gesamte Sammlung von Werten, einen, um alle markierten Elemente zu sammeln, einen anderen, um sie nach demInsertItems
Aufruf wiederherzustellen (beide werden durch eine Prüfung geschütztowner.IsHandleCreated
, wobei der Eigentümer der istListView
), und ruft dann aufBeginUpdate
.ListView.InsertItems
(aus Zeile: 12952), erster Aufruf, hat einen weiteren Durchlauf der gesamten Liste, dann wird ArrayList.AddRange aufgerufen (wahrscheinlich ein weiterer Durchgang dort) und danach ein weiterer Durchgang. Führen zuListView.InsertItems
(ab Zeile: 12952), zweiter Aufruf (überEndUpdate
) einen weiteren Durchgang, bei dem sie zu a hinzugefügt werdenHashTable
, und aDebug.Assert(!listItemsTable.ContainsKey(ItemId))
verlangsamt ihn im Debug-Modus weiter. Wenn das Handle nicht erstellt wird, werden die Elemente zu einemArrayList
,listItemsArray
aber hinzugefügtif (IsHandleCreated)
dann wird es aufgerufenListView.InsertItemsNative
(ab Zeile: 3848) Endgültiger Durchlauf durch die Liste, wo sie tatsächlich zur nativen Listenansicht hinzugefügt wird. einDebug.Assert(this.Items.Contains(li)
verlangsamt zusätzlich die Leistung im Debug-Modus.Es gibt also VIELE zusätzliche Durchgänge durch die gesamte Liste der Elemente im .net-Steuerelement, bevor die Elemente tatsächlich in die native Listenansicht eingefügt werden. Einige der Durchgänge werden durch Überprüfungen des erstellten Handles geschützt. Wenn Sie also Elemente hinzufügen können, bevor das Handle erstellt wird, sparen Sie möglicherweise Zeit. Die
OnHandleCreated
Methode nimmt dielistItemsArray
und AufrufeInsertItemsNative
direkt ohne all den zusätzlichen Aufwand auf.Sie können den
ListView
Code in der Referenzquelle lesen selbst und einen Blick darauf werfen, vielleicht habe ich etwas verpasst.In der März-Ausgabe 2006 des MSDN Magazine gab es einen Artikel mit dem Titel
Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps
.Dieser Artikel enthielt unter anderem Tipps zur Verbesserung der Leistung von ListViews. Es scheint darauf hinzudeuten, dass das Hinzufügen von Elementen vor dem Erstellen des Handles schneller ist, Sie jedoch beim Rendern des Steuerelements einen Preis zahlen. Wenn Sie die in den Kommentaren genannten Rendering-Optimierungen anwenden und die Elemente hinzufügen, bevor das Handle erstellt wird, erhalten Sie möglicherweise das Beste aus beiden Welten.
Bearbeiten: Diese Hypothese wurde auf verschiedene Weise getestet. Das Hinzufügen der Elemente vor dem Erstellen des Handles ist zwar superschnell, beim Erstellen des Handles jedoch exponentiell langsamer. Ich habe versucht, es auszutricksen, um das Handle zu erstellen, und es dann irgendwie dazu gebracht, InsertItemsNative aufzurufen, ohne alle zusätzlichen Durchgänge durchzugehen, aber leider wurde ich vereitelt. Das einzige, was ich für möglich halten könnte, ist, Ihre Win32-ListView in einem C ++ - Projekt zu erstellen, sie mit Elementen zu füllen und mithilfe von Hooking die von ListView beim Erstellen des Handles gesendete CreateWindow-Nachricht zu erfassen und einen Verweis auf win32 zurückzugeben ListView anstelle eines neuen Fensters ... aber wer weiß, was die Seite dort beeinflusst ... ein Win32-Guru müsste über diese verrückte Idee sprechen :)
quelle
Ich habe diesen Code verwendet:
Ich habe auch eingestellt
GenerateMember
für jede Spalte auf false gesetzt.Link zum Sortierer für benutzerdefinierte Listenansichten: http://www.codeproject.com/Articles/5332/ListView-Column-Sorter
quelle
Ich habe das gleiche Problem. Dann fand ich,
sorter
dass es so langsam ist. Machen Sie den Sortierer als nullWenn Sie dann auf Sortierer klicken
ListView_ColumnClick
, machen Sie es auf MethodeNachdem es sortiert wurde, machen Sie endlich wieder die
sorter
Nullquelle
ListView Box Hinzufügen
Dies ist ein einfacher Code, den ich erstellen konnte, um Elemente zu einer Listbox hinzuzufügen, die aus Spalten besteht. Die erste Spalte ist Artikel, während die zweite Spalte Preis ist. Der folgende Code gibt Item Cinnamon in der ersten Spalte und 0,50 in der zweiten Spalte aus.
Keine Instanziierung erforderlich.
quelle
Erstellen Sie alle Ihre Listviewitem FIRST , dann fügen Sie sie in den Listview auf einmal.
Beispielsweise:
quelle