如何加快将项目添加到ListView的速度?


83

我要在WinForms ListView中添加几千个(例如53,709个)项目。

尝试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();

这样比较好,但是仍然慢了一个数量级。让我们将创建ListViewItems与添加ListViewItems分开,以便找到真正的罪魁祸首:

尝试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时都不会尝试对项目进行排序:

    在此处输入图片说明

    检查一下

  • 步骤3-询问stackoverflow:

    在此处输入图片说明

    检查一下

注意:显然,此ListView不在虚拟模式下;由于您不/不能将项目“添加”到虚拟列表视图(您可以将VirtualListSize)。幸运的是,我的问题不是关于虚拟模式下的列表视图。

我有什么丢失的东西可能会导致向列表视图添加项目的速度如此之慢?


奖金Chat不休

我知道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包装器的什么属性?


2
旁注:如果使用复选框,则应在添加到ListView之前设置选中状态。初始化检查状态blogs.msdn.com/b/hippietim/archive/2006/03/20/556007.aspx
Tim Schmelter 2012年

3
我不得不问:为什么要添加那么多项目?
OO

4
很好的问题,伊恩。您是否看到过有关该主题的博客?virtualdub.org/blog/pivot/entry.php?id=273
克里斯·

2
1,349毫秒,不可能。我尝试使用53709件产品,但要花几分钟。为什么要使用包含这么多项目的列表视图?,不是真的可用。您可以使用listBox或comboBox来提高速度,但这是一个疯狂的数字
Xilmiki 2012年

4
为什么不使用虚拟列表视图?好的,您需要编程如何检索项目数据,并且可能必须维护其他内容(例如排序,过滤等),但是无论有多少项目,您都将立即填充列表视图。
卡斯珀拉

Answers:


22

我查看了列表视图的源代码,并注意到一些可能使性能降低4倍的事情,您会看到:

在ListView.cs中,ListViewItemsCollection.AddRange调用ListViewNativeItemCollection.AddRange,这是我开始审核的地方

ListViewNativeItemCollection.AddRange(从第18120行开始)在整个值集合中进行了两次遍历,一次遍历所有已检查的项,另一遍在InsertItems调用后“还原” (它们都受到检查的保护owner.IsHandleCreated,所有者为ListView),然后调用BeginUpdate

ListView.InsertItems(来自第12952行),第一次调用,遍历了整个列表,然后调用了ArrayList.AddRange(可能是另一遍),然后是另一遍。导致

ListView.InsertItems(来自第12952行),第二次调用(通过EndUpdate)另一个将它们添加到的过程HashTable,而aDebug.Assert(!listItemsTable.ContainsKey(ItemId))将在调试模式下进一步降低其速度。如果未创建手柄,则会将项目添加到中ArrayListlistItemsArrayif (IsHandleCreated)会调用

ListView.InsertItemsNative(从第3848行开始)最终通过列表,并将其实际添加到本机listview中。一种Debug.Assert(this.Items.Contains(li)还会降低调试模式下的性能。

因此,在真正将项目插入本机listview之前,.net控件中的整个项目列表都有很多额外的传递。某些通行证通过检查是否创建了Handle来进行保护,因此,如果可以在创建Handle之前添加项目,则可以节省一些时间。该OnHandleCreated方法接受listItemsArray和调用InsertItemsNative直接,而无需大惊小怪。

您可以ListView参考源中阅读代码自己然后看看,也许我错过了一些东西。

在2006年3月的《 MSDN杂志》中有一篇名为Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps

本文包含有关改善ListViews性能的技巧。似乎表明在创建句柄之前添加项目的速度更快,但是在呈现控件时您将付出一定的代价。也许应用注释中提到的渲染优化并在创建句柄之前添加项目将获得两全其美的效果。

编辑:以多种方式测试了该假设,并且在创建手柄之前添加项目的速度很快,而创建手柄的速度却呈指数级降低。我尝试过欺骗它来创建句柄,然后以某种方式让它调用InsertItemsNative而不经历所有额外的传递,但是可惜我被挫败了。我唯一认为可能的是,在c ++项目中创建Win32 ListView,将其填充项目,并在创建其句柄时使用钩子捕获由ListView发送的CreateWindow消息,并将对Win32的引用传递回去。 ListView而不是新窗口..但是谁知道那边会影响什么……Win32专家需要说出这个疯狂的主意:)


10

我使用以下代码:

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


4
是的,在添加项目时激活分类器非常慢***。但是在这种情况下,我没有分类器。但这是有用的第一步,对于可能没有意识到.NET列表视图在每次添加项目时(而不是在结束时)都进行排序的人来说,这是有用的第一步。
伊恩·博伊德

0

我也有同样的问题。然后我发现它sorter变得如此缓慢。将分类器设为空

this.listViewAbnormalList.ListViewItemSorter = null;

然后单击排序器时,在ListView_ColumnClick方法上,使其

 lv.ListViewItemSorter = new ListViewColumnSorter()

最后,将其排序后,sorter再次将其设为null

 ((System.Windows.Forms.ListView)sender).Sort();
 lv.ListViewItemSorter = null;

斯拉夫2建议。我在最初的问题中也建议过这一点。
伊恩·博伊德

是的,与上面的答案相同。:)
巴图尔

-1

ListView Box添加

这是我可以构造的简单代码,用于将项目添加到由列组成的列表框中。第一列是项目,第二列是价格。下面的代码在第一列中显示肉桂色,在第二列中显示0.50。

// How to add ItemName and Item Price
listItems.Items.Add("Cinnamon").SubItems.Add("0.50");

无需实例化。


这是添加一项的简单方法。但这不是添加75,000个项目的快速方法。
伊恩·博伊德

我同意。我将发布一个实现,通过实例化ListView类添加多个结果。
Demetre Phipps

-2

创建所有ListViewItems FIRST,然后将它们添加到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 );
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.