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-リストビューがアイテムを追加するたびにアイテムを並べ替えようとしていないことを確認します。

    ここに画像の説明を入力してください

    小切手

  • ステップ3-スタックオーバーフローを尋ねる:

    ここに画像の説明を入力してください

    小切手

注:明らかに、このListViewは仮想モードではありません。アイテムを仮想リストビューに「追加」できない/できないため(を設定しますVirtualListSize)。幸い、私の質問は仮想モードでのリストビューに関するものではありません。

リストビューへのアイテムの追加が非常に遅い原因となる可能性のある、不足しているものはありますか?


ボーナスチャター

私はそれを行うコードを書くことができるので、WindowsListViewクラスがより良くできることを知っています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
補足:チェックボックスを使用する場合は、リストビューに追加する前にチェック状態を設定する必要があります。初期化は、状態を確認しblogs.msdn.com/b/hippietim/archive/2006/03/20/556007.aspx
ティムSchmelter

3
私は尋ねなければなりません:なぜあなたはそんなに多くのアイテムを追加するのですか?
OO 2012年

4
素晴らしい質問イアン。このテーマについてこのブログを見たことがありますか?virtualdub.org/blog/pivot/entry.php?id=273
Chris

2
1,349ミリ秒、不可能。数分かかる53709アイテムを試してみました。なぜこれほど多くのアイテムを含むリストビューを使用するのですか?、実際には使用できません。listBoxまたはcomboBoxを使用して速度を上げることができますが、これは非常識な数値です
Xilmiki 2012年

4
仮想リストビューを使用してみませんか?さて、あなたはアイテムデータを取得する方法をプログラムする必要があり、ソート、フィルタリングなどの他のものを維持する必要があるかもしれませんが、アイテムの数に関係なくリストビューを即座に埋めます。
カスペラ2012年

回答:


22

リストビューのソースコードを調べたところ、パフォーマンスが4分の1に低下する可能性があることに気づきました。そのため、次のように表示されます。

ListView.csで、ListViewItemsCollection.AddRange呼び出しListViewNativeItemCollection.AddRange、ここから監査を開始しました

ListViewNativeItemCollection.AddRange(行:18120から)値のコレクション全体を2回通過し、1つはすべてのチェック済みアイテムを収集して、InsertItems呼び出された後にそれらを「復元」し(両方ともチェックによって保護されowner.IsHandleCreated、所有者はListView)、を呼び出しますBeginUpdate

ListView.InsertItems(行:12952から)、最初の呼び出しで、リスト全体の別のトラバースがあり、次にArrayList.AddRangeが呼び出され(おそらくそこに別のパス)、その後に別のパスが呼び出されます。につながる

ListView.InsertItems(行:12952から)、2番目の呼び出し(経由EndUpdate)は、それらがに追加される別のパススルーHashTableであり、Debug.Assert(!listItemsTable.ContainsKey(ItemId))デバッグモードではさらに遅くなります。ハンドルが作成されていない場合、それはにアイテムを追加しArrayListlistItemsArrayしかしif (IsHandleCreated)、それは呼び出します

ListView.InsertItemsNative(行:3848から)実際にネイティブリストビューに追加されるリストの最終パス。Debug.Assert(this.Items.Contains(li)さらに、デバッグモードでのパフォーマンスが低下します。

したがって、実際にアイテムをネイティブリストビューに挿入する前に、.netコントロール内のアイテムのリスト全体を通過する余分なパスがたくさんあります。一部のパスは、作成中のハンドルに対するチェックによって保護されているため、ハンドルが作成される前にアイテムを追加できる場合は、時間を節約できる可能性があります。このOnHandleCreatedメソッドは、listItemsArrayを受け取り、InsertItemsNative余分な手間をかけずに直接呼び出します。

自分でリファレンスソースのListViewコードを読んで見てみると、何か見落としているかもしれません。

MSDN Magazineの2006年3月号に、という記事がありましたWinning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps

この記事には、特にListViewsのパフォーマンスを改善するためのヒントが含まれていました。ハンドルが作成される前にアイテムを追加する方が速いことを示しているようですが、コントロールがレンダリングされるときに料金を支払うことになります。おそらく、コメントに記載されているレンダリングの最適化を適用し、ハンドルが作成される前にアイテムを追加すると、両方の長所が得られます。

編集:この仮説をさまざまな方法でテストしました。ハンドルを作成する前にアイテムを追加するのは非常に高速ですが、ハンドルを作成するときは指数関数的に遅くなります。私はそれをだましてハンドルを作成しようと試み、それからどういうわけか余分なパスをすべて通過せずにInsertItemsNativeを呼び出すようにしましたが、残念ながら私は妨害されました。私が考えられる唯一のことは、c ++プロジェクトでWin32ListViewを作成し、アイテムを詰め込み、フックを使用して、ハンドルの作成時に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とても遅くなることに気づきました。ソーターをnullにする

this.listViewAbnormalList.ListViewItemSorter = null;

次に、ソーターをクリックListView_ColumnClickすると、メソッドで、それを作成します

 lv.ListViewItemSorter = new ListViewColumnSorter()

最後に、それがソートされた後、sorter再びnullを作成します

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

Slav2はそれを提案しました。これは、元の質問でも提案しました。
イアン・ボイド

ええ、それは上記の答えと同じです。:)
バトゥール2017年

-1

ListViewボックス追加

これは、列で構成されるリストボックスにアイテムを追加するために作成できた単純なコードです。最初の列はアイテムで、2番目の列は価格です。以下のコードは、アイテムシナモンを最初の列に、0.50を2番目の列に出力します。

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

インスタンス化は必要ありません。


これは、1つのアイテムを追加する簡単な方法です。しかし、そうではありません、高速75,000項目を追加する方法。
イアン・ボイド

同意する。そのListViewクラスをインスタンス化して複数の結果を追加するための実装を投稿します。
DemetrePhipps18年

-2

最初にすべてのListViewItemを 作成してから、それらを一度に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 );
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.