.NET TIPS

コントロールの項目を高速に追加/変更/削除するには?[C#、VB]

デジタルアドバンテージ 遠藤 孝信
2007/09/20

 リストボックスやリストビューなどのコントロールに大量の項目を一度に追加/変更/削除する場合には、そのコントロールの描画を一時的に停止させることにより、高速に行うことができる。

 次のコードは最も単純に、0から29999までの30000万個の数値をリストボックス(listBox1)へ追加し、続けて項目の変更、削除を行う場合の記述例だ。

int ITEMNUM = 30000;

// 項目の追加
for (int i = 0; i < ITEMNUM; i++)
{
  listBox1.Items.Add(i);
}
// 2007ミリ秒

// 項目の変更
for (int i = 0; i < ITEMNUM; i++)
{
  listBox1.Items.Add(i);
}
// 11193ミリ秒

// 項目の削除
for (int i = 0; i < ITEMNUM; i++)
{
  listBox1.Items.RemoveAt(0);
}
// 7294ミリ秒
Dim ITEMNUM As Integer = 30000

' 項目の追加
For i As Integer = 0 To ITEMNUM - 1
  listBox1.Items.Add(i)
Next
' 2007ミリ秒

' 項目の変更
For i As Integer = 0 To ITEMNUM - 1
  listBox1.Items.Add(i)
Next
' 11193ミリ秒

' 項目の削除
For i As Integer = 0 To ITEMNUM - 1
  listBox1.Items.RemoveAt(0)
Next
' 7294ミリ秒
リスト1 リストボックスで30000個の数値を追加/変更/削除
コメントで記した時間は筆者のPCで実行したときの各ループにかかった時間。時間を測定するためのコードは割愛している。

 各ループの最後にコメントで記した時間は、筆者のPC(Intel Core 2 Duo 2.4GHz、メモリ:2Gbytes)でこのコードを実行したときにかかった時間である(以下同様)。

BeginUpdateメソッド/EndUpdateメソッドの利用

 項目の追加/変更/削除時にコントロールの描画を一時的に停止するには、それらの操作の前にBeginUpdateメソッドを呼び出せばよい。そして、描画を再開するには操作完了後にEndUpdateメソッドを呼び出す。

 リスト1のコードに対してBeginUpdateメソッド/EndUpdateメソッドを使用すると次のようになる。

int ITEMNUM = 30000;

// 項目の追加
listBox1.BeginUpdate();
for (int i = 0; i < ITEMNUM; i++)
{
  listBox1.Items.Add(i);
}
listBox1.EndUpdate();
// 1178ミリ秒

// 項目の変更
listBox1.BeginUpdate();
for (int i = 0; i < ITEMNUM; i++)
{
  listBox1.Items.Add(i);
}
listBox1.EndUpdate();
// 9581ミリ秒

// 項目の削除
listBox1.BeginUpdate();
for (int i = 0; i < ITEMNUM; i++)
{
  listBox1.Items.RemoveAt(0);
}
listBox1.EndUpdate();
// 6419ミリ秒
Dim ITEMNUM As Integer = 30000

' 項目の追加
listBox1.BeginUpdate()
For i As Integer = 0 To ITEMNUM - 1
  listBox1.Items.Add(i)
Next
listBox1.EndUpdate()
' 1178ミリ秒

' 項目の変更
listBox1.BeginUpdate()
For i As Integer = 0 To ITEMNUM - 1
  listBox1.Items.Add(i)
Next
listBox1.EndUpdate()
' 9581ミリ秒

' 項目の削除
listBox1.BeginUpdate()
For i As Integer = 0 To ITEMNUM - 1
  listBox1.Items.RemoveAt(0)
Next
listBox1.EndUpdate()
' 6419ミリ秒
リスト2 BeginUpdateメソッド/EndUpdateメソッドを使用した項目の追加/変更/削除

 項目の変更、削除については若干速くなっているだけだが、項目の追加については2007ミリ秒→1178ミリ秒と倍近く速くなっているのが分かる。

Application.DoEventsメソッドによる随時更新

 リスト2では各ループ内での描画を停止しているため、個々の項目の追加などでリストボックスが更新されないのは当然だが、実際にはリスト1でもループ中にはリストボックスの更新は行われない。これは1つの項目の追加/変更/削除ごとに発生する更新のためのメッセージが随時処理されていないためだ。

 1つの項目が追加/変更/削除されるたびにリストボックスが更新されるようにしたければ、Applicationクラス(System.Windows.Forms名前空間)のDoEventsメソッドをループ内で呼び出して、即座にメッセージが処理されるようにする必要がある。

 例えば、項目の追加時であれば次のように記述すればよい(項目の変更/削除時についても同様)。

int ITEMNUM = 30000;

// 項目の追加
for (int i = 0; i < ITEMNUM; i++)
{
  listBox1.Items.Add(i);
  Application.DoEvents();
}
Dim ITEMNUM As Integer = 30000

' 項目の追加
For i As Integer = 0 To ITEMNUM - 1
  listBox1.Items.Add(i)
  Application.DoEvents()
Next
リスト3 Application.DoEventsメソッドにより更新しながら項目を追加

 リストボックスのサイズ(更新が必要な領域の広さ)によるが、当然ながらこのコードはリスト2の場合よりも時間がかかる。

AddRangeメソッドによる項目の追加

 項目の追加に関しては、BeginUpdateメソッド/EndUpdateメソッドを使う以外にも高速化する方法が用意されている。それは、追加する項目をあらかじめ配列にしておき、AddRangeメソッドにより一度に追加する方法だ。記述例は次のようになる。

int ITEMNUM = 30000;

// 項目の追加
object[] data = new object[ITEMNUM];
for (int i = 0; i < ITEMNUM; i++)
{
  data[i] = i;
}
listBox1.Items.AddRange(data);
// 1176ミリ秒
Dim ITEMNUM As Integer = 30000

' 項目の追加
Dim data(ITEMNUM - 1) As Object
For i As Integer = 0 To ITEMNUM - 1
  data(i) = i
Next
listBox1.Items.AddRange(data)
' 1176ミリ秒
リスト4 AddRangeメソッドを使用した項目の追加

 いったん配列に格納するという手間はかかるが、BeginUpdateメソッド/EndUpdateメソッドを使用した場合とほぼ同じ速度で追加が行える。End of Article

カテゴリ:Windowsフォーム 処理対象:コントロール
カテゴリ:Windowsフォーム 処理対象:ListBoxコントロール
使用ライブラリ:ListBoxコントロール(System.Windows.Forms名前空間)
使用ライブラリ:Applicationクラス(System.Windows.Forms名前空間)

この記事と関連性の高い別の.NET TIPS
ListBoxコントロールのオーナー描画(高さ固定)により画像を一覧表示するには?
リストボックスの各項目に値をひも付けるには?
[WPF、Silverlight 2]リストボックスの幅全体に項目を表示するには?
[ASP.NET]DataGridコントロールの削除ボタンで確認メッセージを表示するには?
ListBoxコントロールのオーナー描画(高さ可変)により画像を一覧表示するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間