[解決済み] ListViewへのアイテムの追加を高速化する方法は?
質問
WinFormsのListViewに数千(例:53,709)の項目を追加しています。
試み 1
:
13,870 ms
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
listView.Items.Add(item);
}
これは非常に悪い動作です。明らかな最初の修正は
BeginUpdate/EndUpdate
.
試み2
:
3,106 ms
listView.BeginUpdate();
foreach (Object o in list)
{
ListViewItem item = new ListViewItem();
RefreshListViewItem(item, o);
listView.Items.Add(item);
}
listView.EndUpdate();
これは改善されましたが、まだ一桁以上遅いです。ListViewItemsの作成とListViewItemsの追加を分けて、実際の原因を探ってみましょう。
試み 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()
本当にネックになるのは項目の追加です。試しに変換してみましょう。
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 - 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 wrapperのどのプロパティが足りないのでしょうか?
どのように解決するのですか?
リストビューのソースコードを見てみたところ、あなたが見ているような 4 倍程度のパフォーマンス低下を引き起こす可能性のあるいくつかの事柄に気づきました。
を ListView.cs に追加しました。
ListViewItemsCollection.AddRange
が呼び出されます。
ListViewNativeItemCollection.AddRange
であり、ここから私の監査が始まりました。
ListViewNativeItemCollection.AddRange
(行番号: 18120) は値のコレクション全体を 2 回通過します。1 回はチェックされた項目をすべて収集するため、もう 1 回は
InsertItems
が呼ばれた後にそれらを「復元」するためです (これらは両方とも
owner.IsHandleCreated
であること、オーナーは
ListView
) を呼び出すと
BeginUpdate
.
ListView.InsertItems
(from line: 12952), 最初の呼び出しは、リスト全体の別のトラバースがあり、その後ArrayList.AddRangeが呼び出され(おそらくそこで別のパス)、その後別のパスがあります。次のようになります。
ListView.InsertItems
(行番号: 12952 から)、2回目の呼び出し(via
EndUpdate
を経由して)もう一回通すと、それらは
HashTable
に追加され、さらに
Debug.Assert(!listItemsTable.ContainsKey(ItemId))
はデバッグモードではさらに遅くなります。ハンドルが作成されていない場合は、項目を
ArrayList
,
listItemsArray
しかし
if (IsHandleCreated)
を呼び出すと
ListView.InsertItemsNative
(行: 3848 から) リストを通過する最後のパスで、実際にネイティブのリストビューに追加されます。
Debug.Assert(this.Items.Contains(li)
はさらにデバッグモードでのパフォーマンスを低下させます。
そのため、ネイティブのリストビューに実際にアイテムを挿入する前に、.net コントロールでアイテムのリスト全体を通して多くの余分なパスがあります。 パスの一部は、作成されるハンドルに対するチェックによって保護されているため、ハンドルが作成される前にアイテムを追加できれば、時間を節約できる可能性があります。 その
OnHandleCreated
メソッドは
listItemsArray
を受け取り
InsertItemsNative
を直接呼び出します。
を読むことができます。
ListView
のコードを
参照元
を自分で書いて見てください。何か見落としがあるかもしれません。
MSDN マガジンの 2006 年 3 月号で
という記事がありました。
Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps
.
この記事には、ListViewのパフォーマンスを向上させるためのヒントなどが書かれていました。 ハンドルが作成される前にアイテムを追加することはより高速ですが、コントロールがレンダリングされるときに代償を払うことになることを示しているようです。 おそらく、コメントで言及されているレンダリングの最適化を適用し、ハンドルが作成される前にアイテムを追加すると、両方の長所が得られるでしょう。
編集: この仮説をさまざまな方法でテストしてみましたが、ハンドルを作成する前にアイテムを追加するのは超高速ですが、ハンドルを作成するときは指数関数的に遅くなります。ハンドルを作成し、余分なパスを通さずにInsertItemsNativeを呼び出すようなトリックを試してみましたが、残念ながら失敗しました。 唯一可能性があると思われるのは、c++プロジェクトでWin32 ListViewを作成し、それにアイテムを詰め込み、ハンドルを作成するときにListViewによって送信されるCreateWindowメッセージを捕捉するフックを使用して、新しいウィンドウの代わりにWin32 ListViewへの参照を返すことです。)
関連
-
[解決済み】指定されたキャストが有効でない?
-
[解決済み] エンティティタイプ <type> は、現在のコンテキストのモデルの一部ではありません。
-
[解決済み】ランダムなブーリアンを生成する最速の方法
-
[解決済み】スレッド終了またはアプリケーションの要求により、I/O操作が中断されました。
-
[解決済み] 他のスレッドからGUIを更新するにはどうすればよいですか?
-
[解決済み] enumを列挙するには
-
[解決済み] intをenumにキャストするにはどうすればよいですか?
-
[解決済み] Androidでアクティビティ起動時にEditTextにフォーカスが当たらないようにする方法
-
[解決済み] AndroidのListViewで画像を遅延ロードする方法
-
[解決済み] Eclipseを高速化する方法とは?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] [Entity Framework 4.1でエンティティに関連オブジェクトを追加する際に、エンティティオブジェクトをIEntityChangeTracker.の複数のインスタンスから参照できない。
-
[解決済み】文字列が有効な DateTime " format dd/MM/yyyy " として認識されなかった。
-
[解決済み】トランスポート接続からデータを読み取れない:既存の接続は、リモートホストによって強制的に閉じられました。
-
[解決済み】クロススレッド操作が有効でない。作成されたスレッド以外のスレッドからアクセスされたコントロール
-
[解決済み】ORA-01008: すべての変数がバインドされていません。これらはバインドされています。
-
[解決済み] [Solved] アセンブリ System.Web.Extensions dll はどこにありますか?
-
[解決済み】C# - パスに不正な文字がある場合
-
[解決済み】OnCollisionEnter2Dが実行されない?
-
[解決済み】2つ(またはそれ以上)のリストを1つに統合する(C# .NETで
-
[解決済み】ファイルやアセンブリ、またはその依存関係の1つをロードできませんでした。