ComboBox に List データをバインドする(サンプルあり)

📋 目次(クリックで展開)

Windows Forms 開発において、画面のコンボボックス(ComboBox)に選択肢を表示させる際、独自のクラス(モデルクラス)を要素に持つ List<T> のデータをバインドしたいケースはよくあります。

ComboBox に DataTable をバインドする」と同様に、ComboBox は List<T> などのコレクションもデータソースとして直接受け取ることができます。オブジェクト指向に沿ったクリーンなコードでマスタデータなどを選択させたい場合に有効な手法です。

今回は、独自のクラスリストを ComboBox にバインドする実装手順と、実務で必須となるプロパティの割り当てルール、および値を取り出す際の注意点について解説します。

List データをバインドする仕組み

ComboBox にオブジェクトのリストをバインドする際は、データソース(DataSource)の指定に加えて、以下の2つのプロパティを設定します。

  • DisplayMember:ユーザーが見る画面(ドロップダウンリスト)に、クラスのどのプロパティの文字列を表示するかを指定します。

  • ValueMember:ユーザーが項目を選択した際、システムの裏側でコードやIDとして扱いたいプロパティを指定します。

この2つを文字列で正しく指定することで、画面には「部署名(総務部など)」を見せつつ、プログラムの裏側では「部署コード(D001など)」を管理できるようになります。

実装コード

画面に ComboBox(comboBox1)が配置されているものとします。部署情報を管理する Department クラスのリストを作成し、コンボボックスにバインドするコード例です。

Form1.cs

C#

namespace ComboBox_ListBinding
{
public partial class Form1 : Form
{
// 独自のクラスを持つ List<T> を定義
private List<Department> _departmentList = new List<Department>();
public Form1()
{
InitializeComponent();
InitializeComboBox();
}
private void InitializeComboBox()
{
// 1. マスタデータの準備
_departmentList.Add(new Department("D001", "開発部"));
_departmentList.Add(new Department("D002", "営業部"));
_departmentList.Add(new Department("D003", "総務部"));
// 2. バインドするプロパティ名を文字列で指定
// 画面に表示するプロパティ名
comboBox1.DisplayMember = "Name";
// 裏側で保持する値(キー)のプロパティ名
comboBox1.ValueMember = "Code";
// 3. データソースに List<T> を直接バインド
comboBox1.DataSource = _departmentList;
// 4. 選択が変更されたときのイベントを購読
comboBox1.SelectedIndexChanged += ComboBox1_SelectedIndexChanged;
}
/// <summary>
/// コンボボックスの選択が切り替わったときのイベント
/// </summary>
private void ComboBox1_SelectedIndexChanged(object? sender, EventArgs e)
{
// 選択された項目の「ValueMember」に指定した値を直接取得
if (comboBox1.SelectedValue != null)
{
string selectedCode = comboBox1.SelectedValue.ToString() ?? "";
// ログ出力などで確認用(実務では検索クエリの条件などに利用します)
System.Diagnostics.Debug.WriteLine($"選択された部署コード: {selectedCode}");
}
}
}
/// <summary>
/// コンボボックスに表示したいデータを保持するモデルクラス
/// </summary>
public class Department
{
// フィールドではなく、プロパティ(get; set;)として定義します
public string Code { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public Department(string code, string name)
{
Code = code;
Name = name;
}
}
}

サンプルアプリケーション

ComboBox_ListBinding.zip
※ サンプルアプリケーションは、.NET 10 をターゲットに作成しています。

実行結果

項目の選択を切り替えると、SelectedIndexChanged イベント内で、選択された部署コードが取得できています。

実装のポイント

1. フィールド定義による「クラス名が表示される」バグ

ComboBoxにデータをセットした際、画面のドロップダウンに「プロジェクト名.Department」のようなクラスのフルネームがずらりと表示されてしまうトラブルがよく起こります。この原因は以下の2つのいずれかです。

  • DisplayMember に指定したプロパティ名の文字列がタイポ(打ち間違え)されている。
  • モデルクラスのメンバが、get; set; を持たない「フィールド(通常の変数)」になっている。
// ❌ 画面に表示されずクラス名化してしまうNGな定義
public class Department
{
public string Code; // get; set; がない変数は
public string Name; // データバインディングの解析対象外になります
}

データバインディングの機構は、リフレクションを用いて「Property(プロパティ)」の情報のみをスキャンします。バインド対象のクラスを設計する際は、{ get; set; } を付与してプロパティとして公開してください。

2. 初期化時(データバインド時)の SelectedIndexChanged 登録タイミング

上記のサンプルコードでは、あえて DataSource を設定する直前DisplayMemberValueMember を指定し、かつイベントの購読をバインドの直後に行っています。

もし、イベントの購読(SelectedIndexChanged += ...)を最初に行ってから DataSource を代入すると、データが流し込まれた瞬間にシステムが自動的に先頭行を選択するため、画面が立ち上がる前にイベントが強制的に発火してしまいます。初期化のタイミングで予期せぬ Null 例外やエラーを起こさないためにも、イベントの購読はバインド処理の一番最後に記述するのが実務における安全な順序です。

3. 通常の List<T> の特性とマスタ画面への適性

通常の List<T> には、コード側で _departmentList.Add(...) などを行っても、それを ComboBox 側に通知するイベントがありません。そのため、画面の起動後に選択肢がリアルタイムで増減するような動的な画面には不向きです。コード側で追加した項目をリアルタイムで画面に反映するには、List<T> ではなく、BindingList<T> を利用します。

しかし、今回のような「起動時にデータベースから部署マスタや区分マスタを一度だけ読み込んで固定の選択肢を作る」という静的な用途であれば、軽量な List<T> はメモリ効率も良く、最もシンプルで扱いやすい最適な選択肢となります。

まとめ

今回は、独自のオブジェクトを格納した通常の List<T> データを ComboBox にバインドする手順と、注意すべき仕様について解説しました。

DataTable のときと同様に、バインド構造のルールを守ることで、余計な文字列変換のループ処理を書くことなく、マスタ選択機能を実装することができます。

  • DisplayMember に画面表示用、ValueMember に内部保持用のプロパティ名(文字列)を指定する
  • バインド対象のクラスのメンバはプロパティ({ get; set; })で設計する
  • 初期化時のイベント暴発を防ぐため、イベントの購読は DataSource 指定の直後に行う
  • 静的なマスタ選択肢であれば、通知機能のない通常の List<T> で必要十分に機能する

コンボボックスのデータソース設計を洗練させる際のリファレンスとして、ぜひ活用してみてください。


💬 コメント