C# 如何加速向 ListView 添加项目?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9008310/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to speed adding items to a ListView?
提问by Ian Boyd
i'm adding a few thousand (e.g. 53,709) items to a WinForms ListView.
我正在向 WinForms ListView 添加几千个(例如 53,709 个)项目。
Attempt 1: 13,870 ms
尝试 1:13,870 ms
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
listView.Items.Add(item);
}
This runs very badly. The obvious first fix is to call BeginUpdate/EndUpdate.
这运行非常糟糕。显而易见的第一个解决方法是调用BeginUpdate/EndUpdate.
Attempt 2: 3,106 ms
尝试 2:3,106 ms
listView.BeginUpdate();
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
listView.Items.Add(item);
}
listView.EndUpdate();
This is better, but still an order of magnitude too slow. Let's separate creation of ListViewItems from adding ListViewItems, so we find the actual culprit:
这更好,但仍然太慢了一个数量级。让我们将 ListViewItems 的创建与 ListViewItems 的添加分开,这样我们才能找到真正的罪魁祸首:
Attempt 3: 2,631 ms
尝试 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()
The real bottleneck is adding the items. Let's try converting it to AddRangerather than a foreach
真正的瓶颈是添加项目。让我们尝试将其转换为AddRange而不是foreach
Attempt 4:2,182 ms
尝试 4:2,182 ms
listView.BeginUpdate();
listView.Items.AddRange(items.ToArray());
listView.EndUpdate();
A bit better. Let's be sure that the bottleneck isn't in the ToArray()
好一些。让我们确保瓶颈不在ToArray()
Attempt 5:2,132 ms
尝试 5:2,132 ms
ListViewItem[] arr = items.ToArray();
stopwatch.Start();
listView.BeginUpdate();
listView.Items.AddRange(arr);
listView.EndUpdate();
stopwatch.Stop();
The limitation seems to be adding items to the listview. Maybe the other overload of AddRange, where we add a ListView.ListViewItemCollectionrather than an array
限制似乎是向列表视图添加项目。也许是 的另一个重载AddRange,我们添加了一个ListView.ListViewItemCollection而不是一个数组
Attempt 6:2,141 ms
尝试 6:2,141 ms
listView.BeginUpdate();
ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView);
lvic.AddRange(arr);
listView.EndUpdate();
Well that's no better.
好吧,那也好不到哪儿去。
Now it's time to stretch:
现在是时候拉伸了:
Step 1- make sure no column is set to "auto-width":

Check
Step 2- make sure the ListView isn't trying to sort the items each time i add one:

Check
Step 3- Ask stackoverflow:

Check
第 1 步- 确保没有将列设置为“自动宽度”:

查看
第 2 步- 确保每次添加项目时 ListView 都不会尝试对项目进行排序:

查看
第 3 步- 询问 stackoverflow:

查看
Note:Obviously this ListView is not in virtual mode; since you don't/cannot "add" items to a virtual list view (you set the VirtualListSize). Fortunately my question is not about a list view in virtual mode.
注意:显然这个ListView不是虚拟模式;因为您不/不能将项目“添加”到虚拟列表视图(您设置了VirtualListSize)。幸运的是,我的问题不是关于虚拟模式下的列表视图。
Is there anything i am missing that might account for adding items to the listview being so slow?
有什么我遗漏的东西可能会导致向列表视图添加项目如此缓慢?
Bonus Chatter
奖金喋喋不休
i know the Windows ListView class can do better, because i can write code that does it in 394 ms:
我知道 Windows ListView 类可以做得更好,因为我可以编写以下代码394 ms:
ListView1.Items.BeginUpdate;
for i := 1 to 53709 do
ListView1.Items.Add();
ListView1.Items.EndUpdate;
which when compared to the equivalent C# code 1,349 ms:
与等效的 C# 代码相比1,349 ms:
listView.BeginUpdate();
for (int i = 1; i <= 53709; i++)
listView.Items.Add(new ListViewItem());
listView.EndUpdate();
is an order of magnitude faster.
速度快了一个数量级。
What property of the WinForms ListView wrapper am i missing?
我缺少 WinForms ListView 包装器的什么属性?
采纳答案by Erikest
I took a look at the source code for the list view and I noticed a few things that may make the performance slow down by the factor of 4 or so that you're seeing:
我查看了列表视图的源代码,我注意到一些可能使性能降低 4 倍左右的事情,您会看到:
in ListView.cs, ListViewItemsCollection.AddRangecalls ListViewNativeItemCollection.AddRange, which is where I began my audit
在 ListView.cs 中,ListViewItemsCollection.AddRange调用ListViewNativeItemCollection.AddRange,这是我开始审计的地方
ListViewNativeItemCollection.AddRange(from line: 18120) has two passes through the entire collection of values, one to collect all the checked items another to 'restore' them after InsertItemsis called (they're both guarded by a check against owner.IsHandleCreated, owner being the ListView) then calls BeginUpdate.
ListViewNativeItemCollection.AddRange(来自行:18120)有两次遍历整个值集合,一次收集所有已检查的项目,另一次在InsertItems调用后“恢复”它们(它们都由检查保护owner.IsHandleCreated,所有者是ListView)然后调用BeginUpdate.
ListView.InsertItems(from line: 12952), first call, has another traverse of the entire list then ArrayList.AddRange is called (probably another pass there) then another pass after that. Leading to
ListView.InsertItems(来自行:12952),第一次调用,对整个列表进行另一次遍历,然后调用 ArrayList.AddRange(可能是另一个传递),然后再调用一次。导致
ListView.InsertItems(from line: 12952), second call (via EndUpdate) another pass through where they are added to a HashTable, and a Debug.Assert(!listItemsTable.ContainsKey(ItemId))will slow it further in debug mode. If the handle isn't created, it adds the items to an ArrayList, listItemsArraybut if (IsHandleCreated), then it calls
ListView.InsertItems(来自第 12952 行),第二次调用 (via EndUpdate) 将它们添加到 a 的另一次传递HashTable,并且 aDebug.Assert(!listItemsTable.ContainsKey(ItemId))将在调试模式下进一步减慢速度。如果未创建句柄,则将项目添加到ArrayList, listItemsArraybut if (IsHandleCreated),然后调用
ListView.InsertItemsNative(from line: 3848) final pass through the list where it is actually added to the native listview. a Debug.Assert(this.Items.Contains(li)will additionally slow down performance in debug mode.
ListView.InsertItemsNative(来自第 3848 行)最终通过列表,将其实际添加到本机列表视图中。aDebug.Assert(this.Items.Contains(li)还会降低调试模式下的性能。
So there are A LOT of extra passes through the entire list of items in the .net control before it ever gets to actually inserting the items into the native listview. Some of the passes are guarded by checks against the Handle being created, so if you can add items before the handle is created, it may save you some time. The OnHandleCreatedmethod takes the listItemsArrayand calls InsertItemsNativedirectly without all the extra fuss.
因此,在将项目实际插入本机列表视图之前,在 .net 控件中的整个项目列表中有很多额外的传递。一些通行证通过检查正在创建的句柄来保护,因此如果您可以在创建句柄之前添加项目,则可能会节省一些时间。该OnHandleCreated方法采用listItemsArray和InsertItemsNative直接调用,无需任何额外的大惊小怪。
You can read the ListViewcode in the reference sourceyourself and take a look, maybe I missed something.
你可以自己阅读参考源中的ListView代码看一看,可能是我漏掉了什么。
In the March 2006 issue of MSDN Magazinethere was an article called Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps.
在 2006 年 3 月的 MSDN 杂志中,有一篇名为Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps.
This article contained tips for improving the performance of ListViews, among other things. It seems to indicate that its faster to add items before the handle is created, but that you will pay a price when the control is rendered. Perhaps applying the rendering optimizations mentioned in the comments and adding the items before the handle is created will get the best of both worlds.
本文包含提高 ListViews 性能的技巧等。这似乎表明在创建句柄之前添加项目会更快,但是在呈现控件时您将付出代价。也许应用评论中提到的渲染优化并在创建句柄之前添加项目将获得两全其美的效果。
Edit: Tested this hypothesis in a variety of ways, and while adding the items before creating the handle is suuuper fast, it is exponentially slower when it goes to create the handle. I played with trying to trick it to create the handle, then somehow get it to call InsertItemsNative without going through all the extra passes, but alas I've been thwarted. The only thing I could think might be possible, is to create your Win32 ListView in a c++ project, stuff it with items, and use hooking to capture the CreateWindow message sent by the ListView when creating its handle and pass back a reference to the win32 ListView instead of a new window.. but who knows what the side affects there would be... a Win32 guru would need to speak up about that crazy idea :)
编辑:以多种方式测试了这个假设,虽然在创建句柄之前添加项目非常快,但在创建句柄时却是指数级的慢。我试图欺骗它来创建句柄,然后以某种方式让它调用 InsertItemsNative 而不经过所有额外的传递,但可惜我被挫败了。我认为唯一可能的方法是在 C++ 项目中创建 Win32 ListView,用项目填充它,并在创建其句柄时使用钩子捕获 ListView 发送的 CreateWindow 消息并传回对 win32 的引用ListView 而不是一个新窗口......但是谁知道这会影响到什么......一个 Win32 大师需要说出这个疯狂的想法:)
回答by ahazzah
Create all your ListViewItemsFIRST, then add them to the ListViewall at once.
创建所有ListViewItems FIRST,然后将它们添加到ListView中的一次。
For example:
例如:
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 Slav2
I used this code:
我使用了这个代码:
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();
I have set also GenerateMemberto false for each column.
我还GenerateMember为每一列设置为 false。
Link to custom list view sorter: http://www.codeproject.com/Articles/5332/ListView-Column-Sorter
链接到自定义列表视图排序器:http: //www.codeproject.com/Articles/5332/ListView-Column-Sorter
回答by Batur
I have the same problem. Then I found it's sortermake it so slow.
Make the sorter as null
我也有同样的问题。然后我发现它sorter让它变得如此缓慢。使排序器为空
this.listViewAbnormalList.ListViewItemSorter = null;
then when click sorter, on ListView_ColumnClickmethod , make it
然后当点击排序器时,在ListView_ColumnClick方法上,让它
lv.ListViewItemSorter = new ListViewColumnSorter()
At last, after it's been sorted, make the sorternull again
最后,排序后,sorter再次使空
((System.Windows.Forms.ListView)sender).Sort();
lv.ListViewItemSorter = null;
回答by Demetre Phipps
This is simple code I was able to construct to add Items to a listbox that consist of columns. The first column is item while the second column is price. The code below prints Item Cinnamon in first column and 0.50 in the second column.
这是我能够构建以将项目添加到由列组成的列表框的简单代码。第一列是商品,第二列是价格。下面的代码在第一列中打印 Item Cinnamon,在第二列中打印 0.50。
// How to add ItemName and Item Price
listItems.Items.Add("Cinnamon").SubItems.Add("0.50");
No instantiation needed.
不需要实例化。

