如何加速项目添加到ListView?

我将几千(例如53,709)项目添加到WinForms ListView。

尝试113,870 ms

 foreach (Object o in list) { ListViewItem item = new ListViewItem(); RefreshListViewItem(item, o); listView.Items.Add(item); } 

这运行非常糟糕。 明显的第一个修复是调用BeginUpdate/EndUpdate

尝试23,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了真正的罪魁祸首:

尝试32,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,它将这些项添加到ArrayListlistItemsArray但是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 );