Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何加快向ListView添加项目的速度?_C#_Winforms_Performance_Listview - Fatal编程技术网

C# 如何加快向ListView添加项目的速度?

C# 如何加快向ListView添加项目的速度?,c#,winforms,performance,listview,C#,Winforms,Performance,Listview,我正在向WinForms列表视图添加几千个(例如53709)项 尝试1:13870ms foreach (Object o in list) { ListViewItem item = new ListViewItem(); RefreshListViewItem(item, o); listView.Items.Add(item); } listView.BeginUpdate(); foreach (Object o in list) { ListViewItem

我正在向WinForms列表视图添加几千个(例如53709)项

尝试1
13870ms

foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.BeginUpdate();
foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.EndUpdate();
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()
listView.BeginUpdate();
listView.Items.AddRange(items.ToArray());
listView.EndUpdate();
ListViewItem[] arr = items.ToArray();

stopwatch.Start();

listView.BeginUpdate();
listView.Items.AddRange(arr);
listView.EndUpdate();

stopwatch.Stop();
listView.BeginUpdate();
ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView);
lvic.AddRange(arr);
listView.EndUpdate();
这个运行非常糟糕。显然,第一个修复方法是调用
BeginUpdate/EndUpdate

尝试2
3106 ms

foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.BeginUpdate();
foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.EndUpdate();
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()
listView.BeginUpdate();
listView.Items.AddRange(items.ToArray());
listView.EndUpdate();
ListViewItem[] arr = items.ToArray();

stopwatch.Start();

listView.BeginUpdate();
listView.Items.AddRange(arr);
listView.EndUpdate();

stopwatch.Stop();
listView.BeginUpdate();
ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView);
lvic.AddRange(arr);
listView.EndUpdate();
这是更好的,但仍然是一个数量级太慢。让我们将创建ListViewItems与添加ListViewItems分开,以便找到真正的罪魁祸首:

尝试3
2631ms

foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.BeginUpdate();
foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.EndUpdate();
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()
listView.BeginUpdate();
listView.Items.AddRange(items.ToArray());
listView.EndUpdate();
ListViewItem[] arr = items.ToArray();

stopwatch.Start();

listView.BeginUpdate();
listView.Items.AddRange(arr);
listView.EndUpdate();

stopwatch.Stop();
listView.BeginUpdate();
ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView);
lvic.AddRange(arr);
listView.EndUpdate();
好一点。让我们确保瓶颈不在
ToArray()中

尝试5:
2132毫秒

foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.BeginUpdate();
foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.EndUpdate();
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()
listView.BeginUpdate();
listView.Items.AddRange(items.ToArray());
listView.EndUpdate();
ListViewItem[] arr = items.ToArray();

stopwatch.Start();

listView.BeginUpdate();
listView.Items.AddRange(arr);
listView.EndUpdate();

stopwatch.Stop();
listView.BeginUpdate();
ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView);
lvic.AddRange(arr);
listView.EndUpdate();
限制似乎是向listview添加项。可能是
AddRange
的另一个重载,在这里我们添加了一个
ListView.ListViewItemCollection
,而不是一个数组

尝试6:
2141毫秒

foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.BeginUpdate();
foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.EndUpdate();
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()
listView.BeginUpdate();
listView.Items.AddRange(items.ToArray());
listView.EndUpdate();
ListViewItem[] arr = items.ToArray();

stopwatch.Start();

listView.BeginUpdate();
listView.Items.AddRange(arr);
listView.EndUpdate();

stopwatch.Stop();
listView.BeginUpdate();
ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView);
lvic.AddRange(arr);
listView.EndUpdate();
那也没什么好的

现在是进行伸展运动的时候了:

  • 步骤1-确保没有列设置为“自动宽度”:

    检查

  • 步骤2-确保ListView不会在每次添加项目时尝试对项目进行排序:

    检查

  • 步骤3-询问堆栈溢出:

    检查

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

是否有我遗漏的任何东西可能导致向listview添加项目的速度如此之慢


奖金聊天

我知道Windows ListView类可以做得更好,因为我可以在
394毫秒内编写这样的代码:

ListView1.Items.BeginUpdate;
for i := 1 to 53709 do
   ListView1.Items.Add();
ListView1.Items.EndUpdate;
listView.BeginUpdate();
for (int i = 1; i <= 53709; i++)
   listView.Items.Add(new ListViewItem());
listView.EndUpdate();
与等效的C#code
1349 ms
相比:

ListView1.Items.BeginUpdate;
for i := 1 to 53709 do
   ListView1.Items.Add();
ListView1.Items.EndUpdate;
listView.BeginUpdate();
for (int i = 1; i <= 53709; i++)
   listView.Items.Add(new ListViewItem());
listView.EndUpdate();
listView.BeginUpdate();

对于(int i=1;i我查看了列表视图的源代码,发现了一些可能会使性能降低4倍左右的因素,您可以看到:

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

ListViewNativeItemCollection.AddRange
(从第18120行开始)在整个值集合中有两次传递,一次是收集所有选中的项,另一次是在调用
InsertItems
后“还原”它们(它们都受到针对
所有者的检查的保护。IsHandleCreated
,所有者是
ListView
)然后调用
BeginUpdate

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

ListView.InsertItems
(从第12952行开始),第二次调用(通过
EndUpdate
)另一次传递,将它们添加到
哈希表中,以及
Debug.Assert(!listItemsTable.ContainsKey(ItemId))
将在调试模式下进一步减慢它的速度。如果未创建句柄,它会将项添加到
数组列表
列表项数组
,但
如果(IsHandleCreated)
,则调用

ListView.InsertItemsNactive
(第3848行)最终遍历实际添加到本机ListView的列表。a
Debug.Assert(this.Items.Contains(li)
将进一步降低调试模式下的性能

因此,在.net控件实际将项插入本机listview之前,它会对整个项列表进行大量额外的传递。有些传递是通过检查正在创建的句柄来保护的,因此,如果您可以在创建句柄之前添加项,可能会节省一些时间ted
方法接受
listItemsArray
并直接调用
InsertItemsNactive
,无需任何额外的麻烦

您可以自己阅读
ListView
中的代码并查看,也许我遗漏了什么

有一篇文章叫做《获奖表单:提高Windows表单应用程序性能的实用技巧》

这篇文章包含了一些提高ListView性能的技巧。这似乎表明,在创建句柄之前添加项会更快,但在呈现控件时,您将付出代价。也许应用注释中提到的呈现优化并在创建句柄之前添加项创造将得到最好的两个世界


编辑:以多种方式验证了这一假设,虽然在创建句柄之前添加项的速度很快,但在创建句柄时速度却呈指数级下降。我尝试欺骗它来创建句柄,然后不知何故让它调用InsertItemsNative,而不必经过所有额外的过程,但唉,我已经成功了我认为只有一个可能的事情,就是在C++项目中创建你的Win32 ListVIEW,用它来填充项目,并使用钩子来捕获ListVIEW发送的CREATEVIEW消息,在创建它的句柄并传递一个引用到Win32 ListVIEW而不是一个新窗口。但是谁知道这方面会有什么影响呢?如果你想成为一名Win32高手,你需要大声说出这个疯狂的想法:)

首先创建所有的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 );
var theListView=new ListView();
var items=新的ListViewItem[53709];
对于(int i=0;i
我使用了以下代码:

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();
我还设置了