C#のリストボックス完全ガイド|項目追加・削除・選択取得・データバインドまでわかりやすく解説

はじめに

C#でデスクトップアプリケーションを開発するとき、複数の項目を一覧表示したい場面では「ListBox(リストボックス)」がよく使われます。

ListBoxを利用すると、商品名、ユーザー名、ファイル名、カテゴリー、設定項目などを一覧で表示し、ユーザーが選択した項目を取得できます。設定によっては、複数の項目を同時に選択したり、別のListBoxへ移動したりすることも可能です。

ただし、C#のListBoxにはWindows Forms版とWPF版があり、項目を追加する方法やデータバインドの仕組みに違いがあります。

この記事では、C#のリストボックスについて、項目の追加・削除、選択項目の取得、複数選択、イベント処理、データバインド、表示のカスタマイズまで詳しく解説します。

1. C#のリストボックス(ListBox)とは

ListBoxは、複数の項目を縦方向に一覧表示するためのUIコントロールです。

ユーザーは表示された項目から1件、または複数件を選択できます。項目数が表示領域を超える場合は、スクロールバーを使って残りの項目を確認できます。

C#では、主に次の2種類のListBoxが使われます。

  • Windows FormsのSystem.Windows.Forms.ListBox

  • WPFのSystem.Windows.Controls.ListBox

基本的な目的は同じですが、プロパティやデータバインドの方法、表示をカスタマイズする方法などが異なります。

1-1. ListBoxでできること

C#のListBoxでは、主に次のような処理を実装できます。

  • 文字列やオブジェクトの一覧表示

  • 項目の追加、削除、並べ替え

  • 選択された項目や位置の取得

  • 複数項目の選択

  • 選択変更時のイベント処理

  • 配列やコレクションとのデータバインド

  • 項目ごとの表示デザイン変更

  • ListBox間での項目移動

  • 検索やフィルタリング

単純な文字列一覧だけでなく、商品IDと商品名、社員番号と氏名など、内部値と表示内容を分けて管理することもできます。

1-2. ComboBoxやListViewとの違い

ListBoxと似たコントロールに、ComboBoxやListViewがあります。

ComboBoxは通常、選択候補をドロップダウン形式で表示します。画面上のスペースを節約できるため、都道府県やカテゴリーなど、1件だけ選択させたい場合に適しています。

ListBoxは候補が常に表示されるため、項目を比較しながら選択したい場合や、複数選択させたい場合に向いています。

ListViewは、複数の列、アイコン、詳細情報などを表示する用途に適しています。ファイル名、更新日時、サイズを表形式で表示するようなケースでは、ListBoxよりListViewのほうが使いやすいでしょう。

おおまかには、次のように使い分けます。

コントロール適した用途
ListBox単純な一覧表示、複数選択
ComboBox省スペースな単一選択
ListView複数列やアイコンを含む一覧
DataGrid表形式データの表示・編集

1-3. Windows FormsとWPFのListBoxの違い

Windows Formsでは、ListBoxのItemsコレクションに直接項目を追加する方法が一般的です。

listBox1.Items.Add("りんご");listBox1.Items.Add("みかん");

WPFでもItemsへ追加できますが、実際のアプリケーションではItemsSourceObservableCollection<T>などを設定する方法がよく使われます。

var items = new ObservableCollection<string>{"りんご","みかん"};

listBox1.ItemsSource = items;

また、WPFはXAMLとデータバインドを利用してUIを構築しやすく、ItemTemplateによる柔軟な表示カスタマイズに対応しています。

一方、Windows Formsはイベントハンドラーから直接コントロールを操作するコードが分かりやすく、小規模なツールや既存の業務システムで広く利用されています。

1-4. ListBoxを使うのに適した場面

ListBoxは、次のような場面に適しています。

  • 複数の商品やカテゴリーを一覧表示する

  • ユーザーに複数の項目を選択させる

  • 選択可能な設定項目を常に表示する

  • 左右のリスト間で項目を移動する

  • 選択した項目の詳細を別の領域に表示する

  • ファイルやフォルダーの一覧を表示する

一方、数万件を超える大量データや、多数の列を持つデータを表示する場合は、ListViewやDataGridなども検討する必要があります。

2. ListBoxの基本的な使い方

ここでは、Windows Formsを中心にListBoxの基本操作を解説します。

WPFで異なる操作が必要な場合は、あわせてWPFのコードも紹介します。

2-1. フォームにListBoxを配置する方法

Windows Formsでは、Visual Studioのツールボックスから「ListBox」をフォームへドラッグ&ドロップします。

配置すると、通常はlistBox1という名前が自動的に設定されます。名前はプロパティウィンドウのNameから変更できます。

コードから動的に配置することも可能です。

var listBox = new ListBox{Name = "listBoxProducts",Left = 20,Top = 20,Width = 200,Height = 150};

Controls.Add(listBox);

WPFでは、XAMLに次のように記述します。

<ListBoxx:Name="listBoxProducts"Width="200"Height="150" />

2-2. ListBoxの主要なプロパティ

Windows FormsのListBoxでよく使うプロパティは次のとおりです。

プロパティ内容
ItemsListBoxに表示する項目
SelectedItem現在選択されている項目
SelectedIndex選択項目の位置
SelectedItems複数選択された項目
SelectedIndices複数選択された位置
SelectionMode単一選択・複数選択の設定
DataSourceバインドするデータ
DisplayMember画面に表示するプロパティ
ValueMember内部値として扱うプロパティ
Sorted項目を自動的に並べ替えるか
HorizontalScrollbar横スクロールバーを表示するか

WPFでは、DataSourceの代わりにItemsSourceを使用します。また、内部値を指定する場合はSelectedValuePathを利用します。

2-3. Items・SelectedItem・SelectedIndexの役割

Itemsは、ListBoxに登録されているすべての項目を管理するコレクションです。

int count = listBox1.Items.Count;

SelectedItemは、現在選択されている項目そのものを返します。

object? selectedItem = listBox1.SelectedItem;

SelectedIndexは、選択項目の位置を0から始まる整数で返します。

int index = listBox1.SelectedIndex;

たとえば、先頭の項目が選択されている場合は0、2番目なら1です。何も選択されていない場合は-1になります。

2-4. 初期状態を設定する方法

フォーム表示時に項目を追加する場合は、コンストラクターやForm_Loadイベントで設定します。

public Form1(){InitializeComponent();

listBox1.Items.AddRange(new object[]{"りんご","みかん","ぶどう"});listBox1.SelectedIndex = 0;

}

最初から何も選択しない場合は、項目を追加した後にSelectedIndex-1にします。

listBox1.SelectedIndex = -1;

WPFでは、XAMLで初期項目を定義することもできます。

<ListBox x:Name="listBox1"><ListBoxItem Content="りんご" /><ListBoxItem Content="みかん" /><ListBoxItem Content="ぶどう" /></ListBox>

実際のアプリケーションでは、コードやデータバインドから設定する方法が一般的です。

3. ListBoxに項目を追加する方法

ListBoxには、文字列だけでなく数値や独自クラスのオブジェクトも追加できます。

3-1. Items.Addで項目を1件追加する

1件だけ追加する場合は、Items.Addを使用します。

listBox1.Items.Add("りんご");

テキストボックスに入力された値を追加する例は次のとおりです。

private void buttonAdd_Click(object sender, EventArgs e){string text = textBox1.Text.Trim();

if (text.Length == 0){MessageBox.Show("追加する文字を入力してください。");return;}listBox1.Items.Add(text);textBox1.Clear();

}

Items.Addの戻り値は、追加された項目のインデックスです。

int index = listBox1.Items.Add("バナナ");listBox1.SelectedIndex = index;

3-2. Items.AddRangeで複数項目を一括追加する

複数の項目をまとめて追加する場合は、Items.AddRangeを使用します。

listBox1.Items.AddRange(new object[]{"りんご","みかん","ぶどう"});

ループで1件ずつ追加することもできますが、固定された複数項目を追加するだけならAddRangeのほうが簡潔です。

WPFのItemCollectionには、Windows Formsと同じ形式のAddRangeはありません。複数項目を扱う場合は、コレクションをItemsSourceへ設定する方法が適しています。

3-3. Items.Insertで指定位置に項目を追加する

指定した位置に項目を挿入する場合は、Items.Insertを使用します。

listBox1.Items.Insert(0, "先頭の項目");

2番目の位置に追加する場合は、インデックスに1を指定します。

listBox1.Items.Insert(1, "2番目の項目");

存在しない位置を指定すると例外が発生するため、インデックスが0以上、Items.Count以下であることを確認しましょう。

int index = 2;

if (index >= 0 && index <= listBox1.Items.Count){listBox1.Items.Insert(index, "追加項目");}

3-4. 配列やListから項目を追加する

文字列配列から追加する場合は、AddRangeに渡せる形へ変換します。

string[] fruits ={"りんご","みかん","ぶどう"};

listBox1.Items.AddRange(fruits);

List<string>から追加する場合は、配列へ変換できます。

var fruits = new List<string>{"りんご","みかん","ぶどう"};

listBox1.Items.AddRange(fruits.ToArray());

ループを使用する方法もあります。

foreach (string fruit in fruits){listBox1.Items.Add(fruit);}

3-5. オブジェクトを項目として追加する

ListBoxには、独自クラスのインスタンスも追加できます。

public class Product{public int Id { get; set; }public string Name { get; set; } = string.Empty;

public override string ToString(){return Name;}

}

追加するコードは次のとおりです。

listBox1.Items.Add(new Product{Id = 1,Name = "ノートパソコン"});

listBox1.Items.Add(new Product{Id = 2,Name = "キーボード"});

ToStringをオーバーライドすると、ListBoxには戻り値が表示されます。

Windows Formsでは、DisplayMemberを指定する方法もあります。

listBox1.DisplayMember = nameof(Product.Name);

listBox1.Items.Add(new Product{Id = 1,Name = "ノートパソコン"});

3-6. 重複する項目の追加を防ぐ方法

文字列の重複を防ぐ場合は、Items.Containsで確認します。

string newItem = textBox1.Text.Trim();

if (!listBox1.Items.Contains(newItem)){listBox1.Items.Add(newItem);}else{MessageBox.Show("同じ項目がすでに存在します。");}

大文字と小文字を区別せずに比較する場合は、LINQを利用できます。

bool exists = listBox1.Items.Cast<object>().Any(item => string.Equals(item?.ToString(),newItem,StringComparison.OrdinalIgnoreCase));

if (!exists){listBox1.Items.Add(newItem);}

オブジェクトの場合は、IDなどの一意な値で判定します。

var product = new Product{Id = 1,Name = "ノートパソコン"};

bool exists = listBox1.Items.OfType<Product>().Any(x => x.Id == product.Id);

if (!exists){listBox1.Items.Add(product);}

4. ListBoxの項目を削除・クリアする方法

ListBoxから項目を削除するときは、項目そのものを指定する方法と、インデックスを指定する方法があります。

4-1. Items.Removeで指定した項目を削除する

指定した項目と一致するものを削除する場合は、Items.Removeを使用します。

listBox1.Items.Remove("りんご");

該当する項目が存在しない場合、例外は発生せず、何も削除されません。

選択項目そのものを指定することもできます。

if (listBox1.SelectedItem != null){listBox1.Items.Remove(listBox1.SelectedItem);}

4-2. Items.RemoveAtで指定位置の項目を削除する

インデックスを指定して削除する場合は、Items.RemoveAtを使用します。

listBox1.Items.RemoveAt(0);

有効範囲外のインデックスを指定すると例外が発生します。

int index = 0;

if (index >= 0 && index < listBox1.Items.Count){listBox1.Items.RemoveAt(index);}

4-3. 選択中の項目を削除する

単一選択のListBoxで、現在選択されている項目を削除するコードは次のとおりです。

private void buttonDelete_Click(object sender, EventArgs e){int index = listBox1.SelectedIndex;

if (index == -1){MessageBox.Show("削除する項目を選択してください。");return;}listBox1.Items.RemoveAt(index);

}

削除後に次の項目を選択したい場合は、残りの件数を考慮してインデックスを設定します。

int index = listBox1.SelectedIndex;

if (index >= 0){listBox1.Items.RemoveAt(index);

if (listBox1.Items.Count &gt; 0){listBox1.SelectedIndex =Math.Min(index, listBox1.Items.Count - 1);}

}

4-4. 複数選択された項目をまとめて削除する

複数選択された項目を削除するときは、選択項目を別の配列へコピーしてから削除します。

var selectedItems = listBox1.SelectedItems.Cast<object>().ToArray();

foreach (object item in selectedItems){listBox1.Items.Remove(item);}

インデックスを使う場合は、後ろから削除します。

var selectedIndices = listBox1.SelectedIndices.Cast<int>().OrderByDescending(index => index).ToArray();

foreach (int index in selectedIndices){listBox1.Items.RemoveAt(index);}

前から削除すると、削除するたびに後続項目のインデックスが変化するため、意図しない項目を削除する可能性があります。

4-5. Items.Clearですべての項目を削除する

すべての項目を削除する場合は、Items.Clearを使用します。

listBox1.Items.Clear();

削除前に確認ダイアログを表示する例は次のとおりです。

private void buttonClear_Click(object sender, EventArgs e){if (listBox1.Items.Count == 0){return;}

DialogResult result = MessageBox.Show("すべての項目を削除しますか?","確認",MessageBoxButtons.YesNo,MessageBoxIcon.Question);if (result == DialogResult.Yes){listBox1.Items.Clear();}

}

4-6. 削除時に発生しやすいエラーと対処法

削除時によく発生するのが、SelectedIndex-1の状態でRemoveAtを実行するエラーです。

listBox1.Items.RemoveAt(listBox1.SelectedIndex);

何も選択されていない場合、実際には次の処理になります。

listBox1.Items.RemoveAt(-1);

必ず事前に確認しましょう。

if (listBox1.SelectedIndex >= 0){listBox1.Items.RemoveAt(listBox1.SelectedIndex);}

また、foreachItemsを列挙しながら、同じコレクションから削除することは避けてください。

// 実行中にコレクションが変更されるため不適切foreach (object item in listBox1.Items){listBox1.Items.Remove(item);}

配列へコピーするか、後ろからインデックスを使って削除します。

データバインド中はItemsを直接変更できない場合があります。その場合は、ListBoxではなくバインド元のコレクションを変更してください。

5. ListBoxで選択された項目を取得する方法

ListBoxで選択されたデータは、SelectedItemSelectedIndexSelectedValueなどから取得できます。

5-1. SelectedItemで選択項目を取得する

SelectedItemは、選択されている項目そのものを返します。

if (listBox1.SelectedItem != null){MessageBox.Show(listBox1.SelectedItem.ToString());}

文字列だけを格納している場合は、型変換して取得できます。

string? selectedText = listBox1.SelectedItem as string;

if (selectedText != null){MessageBox.Show(selectedText);}

5-2. SelectedIndexで選択位置を取得する

選択された項目の位置は、SelectedIndexで取得します。

int index = listBox1.SelectedIndex;

if (index >= 0){MessageBox.Show($"選択位置は{index}です。");}

画面上の「1番目、2番目」という形式で表示したい場合は、1を加算します。

int displayPosition = listBox1.SelectedIndex + 1;

5-3. SelectedValueで選択項目の値を取得する

SelectedValueは、ValueMemberで指定したプロパティの値を返します。

var products = new List<Product>{new Product { Id = 1, Name = "ノートパソコン" },new Product { Id = 2, Name = "キーボード" }};

listBox1.DataSource = products;listBox1.DisplayMember = nameof(Product.Name);listBox1.ValueMember = nameof(Product.Id);

選択された商品のIDは次のように取得できます。

if (listBox1.SelectedValue != null){int productId = Convert.ToInt32(listBox1.SelectedValue);MessageBox.Show($"商品ID:{productId}");}

WPFでは、SelectedValuePathを指定します。

<ListBoxx:Name="listBoxProducts"DisplayMemberPath="Name"SelectedValuePath="Id" />
int productId = (int)listBoxProducts.SelectedValue;

5-4. 未選択かどうかを判定する

未選択状態は、次のいずれかで判定できます。

if (listBox1.SelectedItem == null){MessageBox.Show("項目が選択されていません。");}

または、SelectedIndex-1か確認します。

if (listBox1.SelectedIndex == -1){MessageBox.Show("項目が選択されていません。");}

複数選択の場合は、SelectedItems.Countを確認できます。

if (listBox1.SelectedItems.Count == 0){MessageBox.Show("項目が選択されていません。");}

5-5. 選択項目を文字列として取得する

文字列として取得するだけなら、ToStringを使用できます。

string? selectedText = listBox1.SelectedItem?.ToString();

nullの場合に空文字を設定するなら、null合体演算子を使います。

string selectedText =listBox1.SelectedItem?.ToString() ?? string.Empty;

ただし、ListBoxに独自オブジェクトを登録している場合、ToStringが期待する表示にならないことがあります。その場合は、型変換してプロパティを直接取得してください。

5-6. 選択したオブジェクトのプロパティを取得する

Productオブジェクトを格納している場合は、パターンマッチングを利用できます。

if (listBox1.SelectedItem is Product product){int id = product.Id;string name = product.Name;

MessageBox.Show($"ID:{id}\n商品名:{name}");

}

WPFでも同様に取得できます。

if (listBoxProducts.SelectedItem is Product product){MessageBox.Show(product.Name);}

無理に文字列へ変換するより、元の型として取得したほうが、安全で分かりやすいコードになります。

6. ListBoxで複数選択を扱う方法

ListBoxは、設定を変更することで複数の項目を選択できます。

6-1. SelectionModeで複数選択を有効にする

Windows FormsのSelectionModeには、主に次の値があります。

動作
One1件だけ選択
MultiSimpleクリックで複数選択
MultiExtendedCtrlキーやShiftキーを使って複数選択
None選択不可

一般的な複数選択にする場合は、MultiExtendedを指定します。

listBox1.SelectionMode = SelectionMode.MultiExtended;

WPFでは次のように指定します。

<ListBox SelectionMode="Extended" />

WPFの主な設定値はSingleMultipleExtendedです。

6-2. SelectedItemsで選択項目をすべて取得する

複数選択された項目は、SelectedItemsで取得できます。

foreach (object item in listBox1.SelectedItems){Console.WriteLine(item);}

文字列の一覧に変換する例は次のとおりです。

List<string> selectedItems = listBox1.SelectedItems.Cast<object>().Select(item => item.ToString() ?? string.Empty).ToList();

独自オブジェクトの場合は、OfType<T>が便利です。

List<Product> selectedProducts = listBox1.SelectedItems.OfType<Product>().ToList();

6-3. SelectedIndicesで選択位置をすべて取得する

選択項目のインデックスは、SelectedIndicesから取得できます。

foreach (int index in listBox1.SelectedIndices){Console.WriteLine(index);}

配列に変換することも可能です。

int[] selectedIndices = listBox1.SelectedIndices.Cast<int>().ToArray();

6-4. 複数の選択項目を一括処理する

選択された項目をメッセージとしてまとめて表示する例です。

var names = listBox1.SelectedItems.Cast<object>().Select(item => item.ToString()).Where(text => !string.IsNullOrEmpty(text));

string message = string.Join(Environment.NewLine, names);

MessageBox.Show(message);

オブジェクトのプロパティを更新する場合は、次のように処理できます。

foreach (Product product in listBox1.SelectedItems.OfType<Product>()){product.IsActive = true;}

表示に変更を反映するには、利用しているUIフレームワークやバインド元に応じた更新通知が必要です。

6-5. すべて選択・選択解除を実装する

Windows Formsですべての項目を選択する場合は、SetSelectedを使用します。

for (int i = 0; i < listBox1.Items.Count; i++){listBox1.SetSelected(i, true);}

すべての選択を解除する場合は、ClearSelectedを使用します。

listBox1.ClearSelected();

すべて選択するには、SelectionModeMultiSimpleまたはMultiExtendedである必要があります。

WPFで全項目を選択する場合は、SelectAllを使用できます。

listBox1.SelectAll();

選択解除はUnselectAllです。

listBox1.UnselectAll();

7. ListBoxの選択イベントを処理する方法

ユーザーが選択項目を変更したタイミングで処理を実行するには、選択イベントを利用します。

7-1. SelectedIndexChangedイベントの使い方

Windows Formsでは、SelectedIndexChangedイベントを使用します。

private void listBox1_SelectedIndexChanged(object sender,EventArgs e){if (listBox1.SelectedItem == null){return;}

labelSelected.Text =$"選択中:{listBox1.SelectedItem}";

}

Visual Studioのプロパティウィンドウからイベントを登録するほか、コードから登録することもできます。

listBox1.SelectedIndexChanged +=listBox1_SelectedIndexChanged;

7-2. SelectionChangedイベントの使い方

WPFでは、SelectionChangedイベントを利用します。

<ListBoxx:Name="listBox1"SelectionChanged="ListBox1_SelectionChanged" />
private void ListBox1_SelectionChanged(object sender,SelectionChangedEventArgs e){if (listBox1.SelectedItem is string item){labelSelected.Content = $"選択中:{item}";}}

SelectionChangedEventArgsでは、追加選択された項目と選択解除された項目を取得できます。

foreach (object item in e.AddedItems){Console.WriteLine($"選択:{item}");}

foreach (object item in e.RemovedItems){Console.WriteLine($"解除:{item}");}

7-3. ダブルクリックされた項目を取得する

Windows Formsでは、DoubleClickイベントを使用できます。

private void listBox1_DoubleClick(object sender,EventArgs e){if (listBox1.SelectedItem != null){MessageBox.Show($"ダブルクリック:{listBox1.SelectedItem}");}}

項目が存在しない空白部分でのダブルクリックを除外したい場合は、マウス位置から項目を判定します。

private void listBox1_MouseDoubleClick(object sender,MouseEventArgs e){int index = listBox1.IndexFromPoint(e.Location);

if (index != ListBox.NoMatches){object item = listBox1.Items<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[index]</span>;MessageBox.Show(item.ToString());}

}

WPFでは、MouseDoubleClickイベントを利用します。

private void ListBox1_MouseDoubleClick(object sender,MouseButtonEventArgs e){if (listBox1.SelectedItem != null){MessageBox.Show(listBox1.SelectedItem.ToString());}}

7-4. 選択内容に応じて別コントロールを更新する

選択された商品の情報をテキストボックスへ表示する例です。

private void listBoxProducts_SelectedIndexChanged(object sender,EventArgs e){if (listBoxProducts.SelectedItem is not Product product){textBoxId.Clear();textBoxName.Clear();return;}

textBoxId.Text = product.Id.ToString();textBoxName.Text = product.Name;

}

選択内容に応じて、ボタンの有効・無効を切り替えることもできます。

private void listBox1_SelectedIndexChanged(object sender,EventArgs e){buttonDelete.Enabled =listBox1.SelectedIndex >= 0;}

7-5. イベントが意図せず複数回発生する場合の対処法

項目の再設定、データバインド、選択解除などを行うと、選択イベントが複数回発生することがあります。

処理中フラグを使う方法が簡単です。

private bool _isUpdating;

private void ReloadItems(){_isUpdating = true;

try{listBox1.Items.Clear();listBox1.Items.AddRange(new object[] { "A", "B", "C" });listBox1.SelectedIndex = 0;}finally{_isUpdating = false;}

}

private void listBox1_SelectedIndexChanged(object sender,EventArgs e){if (_isUpdating){return;}

// 通常の選択変更処理

}

一時的にイベントを解除する方法もあります。

listBox1.SelectedIndexChanged -=listBox1_SelectedIndexChanged;

try{listBox1.DataSource = newData;}finally{listBox1.SelectedIndexChanged +=listBox1_SelectedIndexChanged;}

イベントを再登録し忘れないよう、tryfinallyを使うと安全です。

8. ListBoxをデータバインドする方法

データバインドを利用すると、コレクションの内容をListBoxへ効率よく表示できます。

8-1. DataSourceに配列やListを設定する

Windows Formsでは、DataSourceに配列やList<T>を設定できます。

string[] fruits ={"りんご","みかん","ぶどう"};

listBox1.DataSource = fruits;

List<string>の場合も同様です。

var fruits = new List<string>{"りんご","みかん","ぶどう"};

listBox1.DataSource = fruits;

ただし、通常のList<T>へ後から項目を追加しても、ListBoxへ自動反映されないことがあります。

8-2. DisplayMemberで表示項目を指定する

オブジェクトの特定プロパティを画面に表示する場合は、DisplayMemberを使用します。

var products = new List<Product>{new Product { Id = 1, Name = "ノートパソコン" },new Product { Id = 2, Name = "キーボード" }};

listBox1.DataSource = products;listBox1.DisplayMember = nameof(Product.Name);

設定順序による一時的な表示変化を避けたい場合は、先にDisplayMemberValueMemberを設定してからDataSourceを指定します。

listBox1.DisplayMember = nameof(Product.Name);listBox1.ValueMember = nameof(Product.Id);listBox1.DataSource = products;

8-3. ValueMemberで内部値を指定する

表示する名称とは別に、IDなどの内部値を管理する場合はValueMemberを設定します。

listBox1.DisplayMember = nameof(Product.Name);listBox1.ValueMember = nameof(Product.Id);listBox1.DataSource = products;

選択されたIDはSelectedValueから取得します。

if (listBox1.SelectedValue is int id){MessageBox.Show($"選択されたID:{id}");}

データソースや型によっては、Convert.ToInt32を利用します。

int id = Convert.ToInt32(listBox1.SelectedValue);

8-4. BindingListを使って変更を画面に反映する

Windows Formsで項目の追加や削除を画面へ反映させたい場合は、BindingList<T>が便利です。

private readonly BindingList<Product> _products = new();

public Form1(){InitializeComponent();

listBox1.DisplayMember = nameof(Product.Name);listBox1.ValueMember = nameof(Product.Id);listBox1.DataSource = _products;_products.Add(new Product{Id = 1,Name = "ノートパソコン"});

}

ボタンから追加する場合も、ItemsではなくBindingListを変更します。

private void buttonAdd_Click(object sender,EventArgs e){_products.Add(new Product{Id = 2,Name = "キーボード"});}

削除も同様です。

if (listBox1.SelectedItem is Product product){_products.Remove(product);}

オブジェクトのプロパティ変更まで自動反映したい場合は、対象クラスでINotifyPropertyChangedを実装する方法があります。

8-5. ObservableCollectionをWPFのListBoxにバインドする

WPFでは、ObservableCollection<T>を利用する方法が一般的です。

public ObservableCollection<Product> Products { get; }= new(){new Product { Id = 1, Name = "ノートパソコン" },new Product { Id = 2, Name = "キーボード" }};

コードビハインドで設定する場合は、次のように記述します。

public MainWindow(){InitializeComponent();listBoxProducts.ItemsSource = Products;}

追加や削除は、ObservableCollectionへ対して行います。

Products.Add(new Product{Id = 3,Name = "マウス"});
if (listBoxProducts.SelectedItem is Product product){Products.Remove(product);}

ObservableCollection<T>はコレクションの追加・削除を通知します。ただし、各オブジェクトのプロパティ変更を通知するには、オブジェクト側でINotifyPropertyChangedを実装する必要があります。

8-6. ItemsSourceを使ったWPFのデータバインド

WPFでは、ViewModelのプロパティをItemsSourceへバインドできます。

<ListBoxItemsSource="{Binding Products}"DisplayMemberPath="Name"SelectedValuePath="Id"SelectedItem="{Binding SelectedProduct}" />

ViewModelの例は次のとおりです。

public class MainViewModel{public ObservableCollection<Product> Products { get; }= new();

public Product? SelectedProduct { get; set; }

}

画面のDataContextへViewModelを設定します。

public MainWindow(){InitializeComponent();DataContext = new MainViewModel();}

MVVMで選択項目の変更を画面外へ通知する場合は、SelectedProductでもINotifyPropertyChangedを実装します。

8-7. データバインド後に項目を追加・削除する際の注意点

Windows FormsでDataSourceを設定した後は、Items.AddItems.Removeを直接使用できない場合があります。

listBox1.DataSource = products;

// データバインド中は直接変更しない// listBox1.Items.Add(new Product());

バインド元を変更してください。

_products.Add(new Product{Id = 3,Name = "マウス"});

通常のList<T>をデータソースとしている場合は、再バインドが必要になることがあります。

products.Add(new Product{Id = 3,Name = "マウス"});

listBox1.DataSource = null;listBox1.DataSource = products;listBox1.DisplayMember = nameof(Product.Name);listBox1.ValueMember = nameof(Product.Id);

ただし、選択位置やスクロール位置がリセットされることがあります。頻繁に変更するなら、BindingList<T>BindingSourceを使うほうが適しています。

WPFでも、ItemsSourceを設定しているときにItemsを直接変更してはいけません。ObservableCollection<T>などのバインド元を変更します。

9. ListBoxの表示をカスタマイズする方法

ListBoxでは、単純な文字列だけでなく、複数の情報や独自デザインを表示できます。

9-1. 項目の表示文字列を変更する

独自オブジェクトを表示する場合、最も簡単なのはToStringをオーバーライドする方法です。

public class Product{public int Id { get; set; }public string Name { get; set; } = string.Empty;

public override string ToString(){return $"{Id}:{Name}";}

}

ただし、ToStringはログ出力やデバッグでも使われるため、画面表示だけを目的に変更すると影響範囲が広くなる場合があります。

Windows FormsではDisplayMember、WPFではDisplayMemberPathItemTemplateを利用するほうが柔軟です。

9-2. オブジェクトの複数プロパティを表示する

Windows Formsで複数プロパティを組み合わせる場合は、表示専用プロパティを用意できます。

public class Product{public int Id { get; set; }public string Name { get; set; } = string.Empty;public decimal Price { get; set; }

public string DisplayText =&gt;$"{Name} {Price:N0}円";

}

listBox1.DisplayMember = nameof(Product.DisplayText);listBox1.DataSource = products;

表示ロジックが複雑な場合は、Windows Formsのオーナードローを利用できます。

9-3. WPFのItemTemplateで表示を整える

WPFでは、ItemTemplateを使って複数の要素を自由に配置できます。

<ListBox ItemsSource="{Binding Products}"><ListBox.ItemTemplate><DataTemplate><StackPanel Margin="4"><TextBlockText="{Binding Name}"FontWeight="Bold" />

            &lt;TextBlockText="{Binding Price, StringFormat={}{0:N0}円}"FontSize="12" /&gt;&lt;/StackPanel&gt;&lt;/DataTemplate&gt;&lt;/ListBox.ItemTemplate&gt;

</ListBox>

横方向に並べる場合は、StackPanelOrientationを変更します。

<StackPanel Orientation="Horizontal"><TextBlockText="{Binding Name}"Width="160" /><TextBlockText="{Binding Price, StringFormat={}{0:N0}円}" /></StackPanel>

9-4. 項目ごとに色やフォントを変更する

Windows Formsでは、DrawModeを設定してDrawItemイベントで描画します。

public Form1(){InitializeComponent();

listBox1.DrawMode = DrawMode.OwnerDrawFixed;listBox1.DrawItem += listBox1_DrawItem;

}

private void listBox1_DrawItem(object? sender,DrawItemEventArgs e){if (e.Index < 0){return;}

e.DrawBackground();object item = listBox1.Items<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[e.Index]</span>;string text = item.ToString() ?? string.Empty;Color color = e.Index % 2 == 0? Color.DarkBlue: Color.DarkGreen;using var brush = new SolidBrush(color);e.Graphics.DrawString(text,e.Font,brush,e.Bounds);e.DrawFocusRectangle();

}

実際には、選択状態の文字色も考慮します。

Color color = (e.State & DrawItemState.Selected) != 0? SystemColors.HighlightText: Color.DarkBlue;

WPFでは、ItemContainerStyleやデータトリガーを利用できます。

<ListBox ItemsSource="{Binding Products}"><ListBox.ItemContainerStyle><Style TargetType="ListBoxItem"><SetterProperty="FontSize"Value="14" />

        &lt;Style.Triggers&gt;&lt;DataTriggerBinding="{Binding IsImportant}"Value="True"&gt;&lt;SetterProperty="Foreground"Value="Red" /&gt;&lt;SetterProperty="FontWeight"Value="Bold" /&gt;&lt;/DataTrigger&gt;&lt;/Style.Triggers&gt;&lt;/Style&gt;&lt;/ListBox.ItemContainerStyle&gt;

</ListBox>

9-5. 横スクロールやスクロールバーを設定する

Windows Formsで横スクロールバーを表示するには、HorizontalScrollbarを有効にします。

listBox1.HorizontalScrollbar = true;

必要に応じて、横方向のスクロール幅を設定します。

listBox1.HorizontalExtent = 500;

WPFでは、ScrollViewerの添付プロパティを使用します。

<ListBoxScrollViewer.HorizontalScrollBarVisibility="Auto"ScrollViewer.VerticalScrollBarVisibility="Auto" />

項目内のテキストを折り返す設定にしている場合、横スクロールバーが表示されないことがあります。

9-6. 項目の高さや幅を調整する

Windows Formsでは、オーナードローを有効にしてItemHeightを設定します。

listBox1.DrawMode = DrawMode.OwnerDrawFixed;listBox1.ItemHeight = 32;

項目ごとに高さを変更する場合は、OwnerDrawVariableを利用します。

listBox1.DrawMode = DrawMode.OwnerDrawVariable;

この場合は、MeasureItemイベントとDrawItemイベントの両方を実装します。

WPFでは、ItemContainerStyleで余白や最小サイズを設定できます。

<ListBox.ItemContainerStyle><Style TargetType="ListBoxItem"><SetterProperty="MinHeight"Value="36" /><SetterProperty="Padding"Value="8,4" /><SetterProperty="HorizontalContentAlignment"Value="Stretch" /></Style></ListBox.ItemContainerStyle>

10. ListBoxの項目を検索・並べ替えする方法

項目数が増える場合は、検索、絞り込み、並べ替えを実装すると操作しやすくなります。

10-1. 入力したキーワードで項目を検索する

Windows Formsには、文字列を検索するFindStringメソッドがあります。

private void buttonSearch_Click(object sender,EventArgs e){string keyword = textBoxSearch.Text;

int index = listBox1.FindString(keyword);if (index &gt;= 0){listBox1.SelectedIndex = index;listBox1.TopIndex = index;}else{MessageBox.Show("一致する項目がありません。");}

}

完全一致を検索する場合は、FindStringExactを使用します。

int index = listBox1.FindStringExact(keyword);

10-2. 前方一致・部分一致で絞り込む

前方一致はStartsWithで判定できます。

string keyword = textBoxSearch.Text.Trim();

var results = originalItems.Where(item => item.StartsWith(keyword,StringComparison.CurrentCultureIgnoreCase)).ToList();

部分一致はContainsを使用します。

var results = originalItems.Where(item => item.Contains(keyword,StringComparison.CurrentCultureIgnoreCase)).ToList();

検索のたびにListBoxの項目を更新する例は次のとおりです。

private readonly List<string> _allItems = new(){"東京都","神奈川県","千葉県","埼玉県"};

private void FilterItems(string keyword){IEnumerable<string> results = _allItems;

if (!string.IsNullOrWhiteSpace(keyword)){results = results.Where(item =&gt;item.Contains(keyword,StringComparison.CurrentCultureIgnoreCase));}listBox1.BeginUpdate();try{listBox1.Items.Clear();listBox1.Items.AddRange(results.ToArray());}finally{listBox1.EndUpdate();}

}

10-3. 一致した項目を選択状態にする

最初に一致した項目を選択する例です。

string keyword = textBoxSearch.Text.Trim();

int index = listBox1.Items.Cast<object>().Select((item, position) => new{Item = item,Position = position}).Where(x => x.Item?.ToString()?.Contains(keyword,StringComparison.CurrentCultureIgnoreCase) == true).Select(x => x.Position).FirstOrDefault(-1);

if (index >= 0){listBox1.SelectedIndex = index;listBox1.TopIndex = index;}

複数一致した項目をすべて選択する場合は、複数選択モードにしてSetSelectedを使用します。

listBox1.ClearSelected();

for (int i = 0; i < listBox1.Items.Count; i++){string text =listBox1.Items[i]?.ToString() ?? string.Empty;

if (text.Contains(keyword,StringComparison.CurrentCultureIgnoreCase)){listBox1.SetSelected(i, true);}

}

10-4. 項目を昇順・降順に並べ替える

Windows Formsで文字列を自動的に昇順表示するだけなら、Sortedを有効にします。

listBox1.Sorted = true;

降順や独自条件で並べ替える場合は、データを並べ替えてから再設定します。

var ascending = listBox1.Items.Cast<object>().OrderBy(item => item?.ToString()).ToArray();

listBox1.Items.Clear();listBox1.Items.AddRange(ascending);

降順の場合はOrderByDescendingを使います。

var descending = listBox1.Items.Cast<object>().OrderByDescending(item => item?.ToString()).ToArray();

オブジェクトは、任意のプロパティで並べ替えられます。

var sortedProducts = products.OrderBy(product => product.Price).ThenBy(product => product.Name).ToList();

10-5. データバインドした項目をフィルタリングする

Windows Formsでは、元のコレクションから絞り込み結果を作って再設定する方法があります。

private List<Product> _allProducts = new();

private void FilterProducts(string keyword){var filtered = _allProducts.Where(product =>product.Name.Contains(keyword,StringComparison.CurrentCultureIgnoreCase)).ToList();

listBox1.DataSource = null;listBox1.DisplayMember = nameof(Product.Name);listBox1.ValueMember = nameof(Product.Id);listBox1.DataSource = filtered;

}

WPFでは、ICollectionViewFilterを使用できます。

ICollectionView view =CollectionViewSource.GetDefaultView(Products);

view.Filter = item =>{if (item is not Product product){return false;}

return product.Name.Contains(keyword,StringComparison.CurrentCultureIgnoreCase);

};

view.Refresh();

並べ替えもICollectionViewに設定できます。

view.SortDescriptions.Clear();

view.SortDescriptions.Add(new SortDescription(nameof(Product.Name),ListSortDirection.Ascending));

11. ListBox間で項目を移動する方法

左右に2つのListBoxを配置し、選択項目を移動するUIは、権限設定や対象選択などでよく使われます。

11-1. 選択項目を別のListBoxへ移動する

単一選択項目を移動する例です。

private void buttonMoveRight_Click(object sender,EventArgs e){object? selectedItem = listBoxLeft.SelectedItem;

if (selectedItem == null){return;}listBoxRight.Items.Add(selectedItem);listBoxLeft.Items.Remove(selectedItem);

}

重複を防ぐ場合は、追加先に存在するか確認します。

if (!listBoxRight.Items.Contains(selectedItem)){listBoxRight.Items.Add(selectedItem);}

listBoxLeft.Items.Remove(selectedItem);

11-2. 複数項目をまとめて移動する

複数項目を移動する場合は、選択項目を配列へコピーします。

private void buttonMoveSelected_Click(object sender,EventArgs e){object[] selectedItems = listBoxLeft.SelectedItems.Cast<object>().ToArray();

foreach (object item in selectedItems){listBoxRight.Items.Add(item);listBoxLeft.Items.Remove(item);}

}

選択中のコレクションを直接列挙しながら削除すると、コレクションが変化して正しく処理できないため注意してください。

11-3. すべての項目を一括移動する

すべての項目を移動する場合も、一度配列へコピーします。

private void buttonMoveAll_Click(object sender,EventArgs e){object[] items = listBoxLeft.Items.Cast<object>().ToArray();

listBoxRight.Items.AddRange(items);listBoxLeft.Items.Clear();

}

重複を除外する場合は、1件ずつ確認します。

foreach (object item in items){if (!listBoxRight.Items.Contains(item)){listBoxRight.Items.Add(item);}}

listBoxLeft.Items.Clear();

データバインドしている場合は、ListBoxのItemsではなく、それぞれのバインド元コレクションを変更します。

11-4. ドラッグ&ドロップで項目を移動する

Windows Formsでドラッグ&ドロップを実装する場合は、移動先のAllowDropを有効にします。

listBoxRight.AllowDrop = true;

移動元でドラッグを開始します。

private void listBoxLeft_MouseDown(object sender,MouseEventArgs e){int index = listBoxLeft.IndexFromPoint(e.Location);

if (index == ListBox.NoMatches){return;}object item = listBoxLeft.Items<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[index]</span>;listBoxLeft.DoDragDrop(item,DragDropEffects.Move);

}

移動先で受け入れ可能か判定します。

private void listBoxRight_DragEnter(object sender,DragEventArgs e){if (e.Data?.GetDataPresent(typeof(string)) == true){e.Effect = DragDropEffects.Move;}}

独自オブジェクトの場合は、その型を指定します。

ドロップ時に移動します。

private void listBoxRight_DragDrop(object sender,DragEventArgs e){object? item = e.Data?.GetData(typeof(string));

if (item == null){return;}if (!listBoxRight.Items.Contains(item)){listBoxRight.Items.Add(item);}listBoxLeft.Items.Remove(item);

}

実際の開発では、移動元を識別する情報や、ドロップ位置の計算も必要になります。

11-5. 項目の順番を上下に入れ替える

選択項目を1つ上へ移動する例です。

private void MoveUp(){int index = listBox1.SelectedIndex;

if (index &lt;= 0){return;}object item = listBox1.Items<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[index]</span>;listBox1.Items.RemoveAt(index);listBox1.Items.Insert(index - 1, item);listBox1.SelectedIndex = index - 1;

}

1つ下へ移動する場合は次のようにします。

private void MoveDown(){int index = listBox1.SelectedIndex;

if (index &lt; 0 ||index &gt;= listBox1.Items.Count - 1){return;}object item = listBox1.Items<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[index]</span>;listBox1.Items.RemoveAt(index);listBox1.Items.Insert(index + 1, item);listBox1.SelectedIndex = index + 1;

}

データバインドしている場合は、バインド元がObservableCollection<T>ならMoveメソッドを利用できます。

Products.Move(oldIndex, newIndex);

12. ListBoxが正しく動かないときの原因と解決策

ListBoxが期待どおりに動かないときは、データバインド、選択状態、スレッド、コレクションの変更方法を確認しましょう。

12-1. 項目を追加しても表示されない

DataSourceItemsSourceを設定している場合、Itemsへ直接追加しても反映されないか、例外が発生することがあります。

Windows Formsでは、バインド元のBindingList<T>などを変更します。

_products.Add(new Product{Id = 10,Name = "追加商品"});

WPFでは、ObservableCollection<T>を変更します。

Products.Add(new Product{Id = 10,Name = "追加商品"});

通常のList<T>を使っている場合、追加通知が発生しません。通知対応コレクションへ変更するか、バインドを再設定してください。

12-2. SelectedItemがnullになる

SelectedItemがnullになる主な原因は、次のとおりです。

  • 項目が選択されていない

  • 選択直後ではなく、選択解除後のイベントが発生した

  • データソースを再設定した

  • 選択項目を削除した

  • 画面の初期化中にイベントが発生した

必ずnullを確認します。

if (listBox1.SelectedItem is not Product product){return;}

初期化中にイベント処理を行いたくない場合は、更新フラグを利用します。

12-3. SelectedIndexがマイナス1になる

SelectedIndex-1はエラーではなく、何も選択されていないことを表します。

if (listBox1.SelectedIndex == -1){return;}

初期表示で選択させたい場合は、項目追加後に設定します。

if (listBox1.Items.Count > 0){listBox1.SelectedIndex = 0;}

データソースの再設定直後は選択状態が変わるため、再設定後に選択位置を指定してください。

12-4. DisplayMemberやValueMemberが反映されない

プロパティ名のスペルや大文字・小文字が間違っていないか確認します。

文字列を直接書くより、nameofを使うと安全です。

listBox1.DisplayMember = nameof(Product.Name);listBox1.ValueMember = nameof(Product.Id);

対象プロパティは、通常publicで読み取り可能である必要があります。

public string Name { get; set; } = string.Empty;

WPFでは、DisplayMemberPathSelectedValuePathを使用します。

<ListBoxDisplayMemberPath="Name"SelectedValuePath="Id" />

ItemTemplateを設定している場合は、DisplayMemberPathと競合する構成を避け、どちらか一方に統一すると分かりやすくなります。

12-5. データを変更しても表示が更新されない

コレクションへ項目を追加しても反映されない場合は、通知機能を持つコレクションを利用します。

  • Windows Forms:BindingList<T>

  • WPF:ObservableCollection<T>

オブジェクトのプロパティを変更しても反映されない場合は、INotifyPropertyChangedを実装します。

public class Product : INotifyPropertyChanged{private string _name = string.Empty;

public int Id { get; set; }public string Name{get =&gt; _name;set{if (_name == value){return;}_name = value;PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(nameof(Name)));}}public event PropertyChangedEventHandler? PropertyChanged;

}

Windows Formsでは、バインド方法によってBindingSource.ResetBindingsを使用するケースもあります。

bindingSource1.ResetBindings(false);

12-6. コレクション変更時に例外が発生する

列挙中のコレクションを変更すると、例外や項目の取りこぼしが発生します。

次のようなコードは避けます。

foreach (object item in listBox1.Items){listBox1.Items.Remove(item);}

配列へコピーしてから処理します。

object[] items = listBox1.Items.Cast<object>().ToArray();

foreach (object item in items){listBox1.Items.Remove(item);}

インデックスを使う場合は後ろから削除します。

for (int i = listBox1.Items.Count - 1; i >= 0; i--){if (ShouldDelete(listBox1.Items[i])){listBox1.Items.RemoveAt(i);}}

12-7. UIが固まる場合の非同期処理とスレッド対策

データベース、ファイル、Web APIなどから大量データを取得する処理をUIスレッドで実行すると、画面が固まります。

時間のかかる処理は非同期化します。

private async void buttonLoad_Click(object sender,EventArgs e){buttonLoad.Enabled = false;

try{List&lt;Product&gt; products =await LoadProductsAsync();listBox1.DisplayMember = nameof(Product.Name);listBox1.ValueMember = nameof(Product.Id);listBox1.DataSource = products;}catch (Exception ex){MessageBox.Show($"読み込みに失敗しました。\n{ex.Message}");}finally{buttonLoad.Enabled = true;}

}

バックグラウンドスレッドから、Windows FormsのListBoxを直接操作してはいけません。

Invoke(() =>{listBox1.Items.Add("追加項目");});

WPFでは、Dispatcherを利用します。

Dispatcher.Invoke(() =>{Products.Add(new Product{Id = 1,Name = "追加商品"});});

大量の項目をWindows Formsへ直接追加するときは、BeginUpdateEndUpdateで再描画を抑制できます。

listBox1.BeginUpdate();

try{foreach (Product product in products){listBox1.Items.Add(product);}}finally{listBox1.EndUpdate();}

13. C#のListBoxに関するよくある質問

13-1. ListBoxの項目数を取得するには?

Windows Formsでは、Items.Countを使用します。

int count = listBox1.Items.Count;

WPFでも同様です。

int count = listBox1.Items.Count;

データバインドしている場合は、バインド元のコレクション数を取得したほうが、アプリケーションの設計として分かりやすいことがあります。

int count = Products.Count;

13-2. 特定の項目が存在するか確認するには?

完全一致で確認する場合は、Items.Containsを利用できます。

bool exists = listBox1.Items.Contains("りんご");

部分一致や大文字・小文字を区別しない検索には、LINQを使います。

bool exists = listBox1.Items.Cast<object>().Any(item => string.Equals(item?.ToString(),"りんご",StringComparison.CurrentCultureIgnoreCase));

オブジェクトの場合は、IDなどで判定します。

bool exists = listBox1.Items.OfType<Product>().Any(product => product.Id == 10);

13-3. 初期表示で項目を選択しないようにするには?

項目やデータソースを設定した後に、SelectedIndex-1にします。

listBox1.DataSource = products;listBox1.SelectedIndex = -1;

WPFでは、SelectedIndex="-1"を指定できます。

<ListBoxItemsSource="{Binding Products}"SelectedIndex="-1" />

初期化の過程で自動選択される場合は、データ設定後に選択解除してください。

13-4. 選択中の項目を変更できないようにするには?

Windows Formsで選択自体を禁止する場合は、SelectionMode.Noneを設定します。

listBox1.SelectionMode = SelectionMode.None;

ただし、既存の選択を固定したままユーザー操作だけを防ぎたい場合は、ListBox全体を無効にする方法があります。

listBox1.Enabled = false;

無効化すると表示色も変わります。

特定の条件で選択変更を戻す方法もありますが、イベントの再発生や操作性に注意が必要です。閲覧専用の一覧であれば、ListBox以外のコントロールも検討しましょう。

13-5. 大量データを高速に表示するには?

Windows Formsでは、次の対策が有効です。

  • BeginUpdateEndUpdateで再描画を抑制する

  • 1件ずつではなく、AddRangeを利用する

  • 不要なイベント処理を一時的に停止する

  • データ取得を非同期化する

  • 必要な項目だけを表示する

  • 仮想モードを持つ別コントロールを検討する

listBox1.BeginUpdate();

try{listBox1.Items.Clear();listBox1.Items.AddRange(items);}finally{listBox1.EndUpdate();}

WPFのListBoxでは、通常、仮想化対応のパネルが利用されます。ただし、パネルの変更、項目のグループ化、スクロール設定などによって仮想化が無効になる場合があります。

<ListBoxItemsSource="{Binding Products}"VirtualizingPanel.IsVirtualizing="True"VirtualizingPanel.VirtualizationMode="Recycling"ScrollViewer.CanContentScroll="True" />

非常に大量のデータを表示する場合は、ページング、検索による絞り込み、DataGridなども検討してください。

13-6. WinFormsとWPFではどちらのListBoxを使うべき?

新規開発で、柔軟なデザイン、データバインド、MVVMを重視するならWPFが適しています。

WPFのListBoxは、ItemTemplate、スタイル、データトリガーなどを使って、複雑な表示を比較的整理して実装できます。

一方、既存のWindows Formsアプリケーションを改修する場合や、シンプルな社内ツールを短期間で構築する場合は、Windows Formsが扱いやすいことがあります。

ListBoxだけで比較するのではなく、次の点を考慮して選択しましょう。

  • 既存システムで使われているUI技術

  • 開発チームの経験

  • 必要なデザインの自由度

  • データバインドの複雑さ

  • アプリケーションの保守期間

  • MVVMなどの設計パターンを採用するか

まとめ

C#のリストボックスは、複数の項目を一覧表示し、選択、追加、削除、検索などを実装できる便利なコントロールです。

Windows Formsでは、Items.AddItems.RemoveSelectedItemSelectedIndexなどを使うことで、基本的な操作を簡単に実装できます。

複数選択を扱う場合は、SelectionModeを設定し、SelectedItemsSelectedIndicesから選択内容を取得します。

データと画面を連携させる場合は、Windows FormsではDataSourceDisplayMemberValueMemberを使用します。更新を自動反映させたい場合は、BindingList<T>BindingSourceが便利です。

WPFでは、ItemsSourceObservableCollection<T>をバインドし、ItemTemplateで表示を整える方法が一般的です。項目のプロパティ変更を反映する場合は、INotifyPropertyChangedも実装します。

C#のListBoxを正しく使うために、特に次の点を押さえておきましょう。

  • 未選択時のSelectedIndex-1

  • 未選択時のSelectedItemはnull

  • 複数項目を削除するときは配列へコピーするか、後ろから削除する

  • データバインド中はItemsではなくバインド元を変更する

  • 頻繁に更新するデータには通知対応コレクションを使う

  • 時間のかかる処理は非同期化し、UI更新はUIスレッドで行う

これらを理解すれば、単純な一覧表示だけでなく、検索、並べ替え、複数選択、項目移動、データバインドを備えた実用的な画面を構築できます。