如何加速项目添加到ListView?
我将几千(例如53,709)项目添加到WinForms ListView。
尝试1 : 13,870 ms
foreach (Object o in list) { ListViewItem item = new ListViewItem(); RefreshListViewItem(item, o); listView.Items.Add(item); }
这运行非常糟糕。 明显的第一个修复是调用BeginUpdate/EndUpdate
。
尝试2 : 3,106 ms
listView.BeginUpdate(); foreach (Object o in list) { ListViewItem item = new ListViewItem(); RefreshListViewItem(item, o); listView.Items.Add(item); } listView.EndUpdate();
这样比较好,但是还有一个数量级太慢了。 让我们分开创buildListViewItems和添加ListViewItems,所以我们find了真正的罪魁祸首:
尝试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()
真正的瓶颈是添加项目。 让我们尝试将其转换为AddRange
而不是一个foreach
尝试4: 2,182 ms
listView.BeginUpdate(); listView.Items.AddRange(items.ToArray()); listView.EndUpdate();
好一些。 让我们确定瓶颈不在ToArray()
尝试5: 2,132 ms
ListViewItem[] arr = items.ToArray(); stopwatch.Start(); listView.BeginUpdate(); listView.Items.AddRange(arr); listView.EndUpdate(); stopwatch.Stop();
限制似乎是将项目添加到列表视图。 也许AddRange
的其他重载,我们添加一个ListView.ListViewItemCollection
而不是一个数组
尝试6: 2,141 ms
listView.BeginUpdate(); ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView); lvic.AddRange(arr); listView.EndUpdate();
那么这不是更好。
现在是时候了:
-
第1步 – 确保没有列设置为“自动宽度” :
检查
-
第2步 – 确保ListView不尝试每次我添加一个项目时sorting:
检查
-
第3步 – 问stackoverflow:
检查
注意:显然这个ListView不是处于虚拟模式; 因为你没有/不能“添加”项目到虚拟列表视图(你设置VirtualListSize
)。 幸运的是,我的问题不是关于虚拟模式下的列表视图。
有什么我失踪,可能帐户添加项目列表视图是如此缓慢?
奖金喋喋不休
我知道Windows ListView类可以做得更好,因为我可以编写代码,它在394 ms
:
ListView1.Items.BeginUpdate; for i := 1 to 53709 do ListView1.Items.Add(); ListView1.Items.EndUpdate;
当与等效的C#代码相比时,这是1,349 ms
:
listView.BeginUpdate(); for (int i = 1; i <= 53709; i++) listView.Items.Add(new ListViewItem()); listView.EndUpdate();
是一个数量级更快。
什么属性的WinForms ListView包装我错过了?
我查看了列表视图的源代码,发现了一些可能导致性能下降了4倍的东西,你会发现:
在ListView.cs中, ListViewItemsCollection.AddRange
调用ListViewNativeItemCollection.AddRange
,这是我开始审计的地方
ListViewNativeItemCollection.AddRange
(从行:18120)有两个通过整个值的集合,一个是收集所有的检查项目,以便在调用InsertItems
之后“还原”它们(它们都被一个针对owner.IsHandleCreated
的检查所owner.IsHandleCreated
,所有者是ListView
)然后调用BeginUpdate
。
ListView.InsertItems
(从行:12952),第一次调用,有整个列表的另一遍历,然后调用ArrayList.AddRange(可能是另一个通过),然后再次通过。 导致
ListView.InsertItems
(从行:12952),第二次调用(通过EndUpdate
)另一个通过他们被添加到HashTable
,并且Debug.Assert(!listItemsTable.ContainsKey(ItemId))
将在debugging模式下进一步减慢它。 如果句柄没有被创build,它将这些项添加到ArrayList
, listItemsArray
但是if (IsHandleCreated)
,那么它调用
ListView.InsertItemsNative
(从行:3848)最后通过列表实际添加到本机列表视图。 Debug.Assert(this.Items.Contains(li)
将在debugging模式下进一步降低性能。
因此,在实际将项目插入本机列表视图之前,需要在.net控件的整个项目列表中添加大量额外的通行证。 其中一些通行证是通过对创build的句柄进行检查来保护的,所以如果您可以在创build句柄之前添加项目,它可以为您节省一些时间。 OnHandleCreated
方法接受listItemsArray
并直接调用InsertItemsNative
而不用额外的大惊小怪。
您可以自己阅读参考源中的ListView
代码,并看看,也许我错过了一些东西。
在2006年3月的MSDN杂志上,有一篇名为Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps
的文章Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps
。
本文包含提高ListViews的性能等方面的提示。 这似乎表明,在创build句柄之前添加项目的速度会更快,但是在呈现控件时您将付出代价。 也许应用在注释中提到的渲染优化和在创build句柄之前添加项目将获得两全其美。
编辑:以各种方式testing了这个假设,并且在创build句柄之前添加项目的速度非常快,创build句柄时速度指数较慢。 我试图欺骗它来创build句柄,然后以某种方式让它调用InsertItemsNative而不通过所有额外的通行证,但唉,我已经挫败。 我唯一能想到的可能就是在c ++项目中创build你的Win32 ListView,用项目填充它,并使用钩子捕获ListView在创build句柄时发送的CreateWindow消息,并将引用传回给win32 ListView而不是一个新的窗口..但谁知道什么副作用会有…一个Win32的大师将需要说出这个疯狂的想法:)
我用这个代码:
ResultsListView.BeginUpdate(); ResultsListView.ListViewItemSorter = null; ResultsListView.Items.Clear(); //here we add items to listview //adding item sorter back ResultsListView.ListViewItemSorter = lvwColumnSorter; ResultsListView.Sort(); ResultsListView.EndUpdate();
我也设置每个列的GenerateMember
为false。
链接到自定义列表视图分类器: http : //www.codeproject.com/Articles/5332/ListView-Column-Sorter
我也有同样的问题。 然后,我发现它是sorter
使它如此缓慢。 使分拣机为空
this.listViewAbnormalList.ListViewItemSorter = null;
然后当点击sorting器时,在ListView_ColumnClick
方法上做出来
lv.ListViewItemSorter = new ListViewColumnSorter()
最后,在sorting之后,再次使sorter
空
((System.Windows.Forms.ListView)sender).Sort(); lv.ListViewItemSorter = null;
首先创build所有ListViewItems ,然后将它们一次添加到ListView中。
例如:
var theListView = new ListView(); var items = new ListViewItem[ 53709 ]; for ( int i = 0 ; i < items.Length; ++i ) { items[ i ] = new ListViewItem( i.ToString() ); } theListView.Items.AddRange( items );