C# ComboBoxのDisplayMember / ValueMemberが表示されない原因【2013年コードを.NET 10で再検証】

スポンサーリンク

30秒で結論

Windows Formsの`ComboBox`に`List`をバインドし、`DisplayMember`や`ValueMember`を指定する場合、指定する名前は**データソース側のpublicプロパティ**として公開されている必要があります。

HIROs.NETでは2013年に、次のようなpublicフィールドを使ったコードでハマった記録があります。

public class Employee
{
    public int Empno;
    public string Name;
}

これをプロパティへ変更すると解決しました。

public class Employee
{
    public int Empno { get; set; }
    public string Name { get; set; } = "";
}

Microsoft Learnの現在のWindows Formsドキュメントでも、`IList`をバインドする場合、`DisplayMember`にはリスト内の型の**publicプロパティ**を指定すると説明されています。

では、2013年のコードは.NET 10でも同じようにハマるのでしょうか。

今回は、当時の記事をほぼそのまま現在の環境へ持ってきて確認します。

こんな症状

次のように設定しているのに、ComboBoxへ期待した名前が表示されません。

comboBox1.DataSource = employees;
comboBox1.ValueMember = "Empno";
comboBox1.DisplayMember = "Name";

ぱっと見ると間違っていないように見えます。

`DataSource`も設定している。
`DisplayMember`も指定している。
`ValueMember`も指定している。

それなのに期待どおりにならない。

2013年当時、原因は`Employee`クラスのメンバーをフィールドとして宣言していたことでした。

2013年の記事で使っていたコード

まずは、当時ハマった形を再現します。

フォームに`ComboBox`を1つ配置し、名前を`comboBox1`とします。

public class Employee
{
    public int Empno;
    public string Name;

    public Employee(int empno, string name)
    {
        Empno = empno;
        Name = name;
    }
}

フォームの読み込み時にデータを設定します。

private void Form1_Load(object sender, EventArgs e)
{
    var employees = new List
    {
        new Employee(1, "山田 太郎"),
        new Employee(2, "佐藤 花子")
    };

    comboBox1.DataSource = employees;
    comboBox1.ValueMember = "Empno";
    comboBox1.DisplayMember = "Name";
}
```

2013年の記事では、この形では期待した表示になりませんでした。

原因

ポイントは`DisplayMember`と`ValueMember`が、単に「同じ名前の何か」を探しているわけではないことです。

Windows Formsのデータバインディングでは、バインド対象の型が持つ**プロパティ**を通して値を取得します。

つまり、

public string Name;

はpublicではありますが、これはフィールドです。

一方、

public string Name { get; set; }

プロパティです。

見た目は似ていますが、データバインディングから見ると別物です。

ふだんコードを書いていると、

employee.Name

と同じようにアクセスできるので、ここはかなり気づきにくいポイントです。

修正版

`Employee`のフィールドをプロパティへ変更します。

public class Employee
{
    public int Empno { get; set; }

    public string Name { get; set; } = "";

    public Employee(int empno, string name)
    {
        Empno = empno;
        Name = name;
    }
}

ComboBox側のコードは同じです。

private void Form1_Load(object sender, EventArgs e)
```csharp
private void Form1_Load(object sender, EventArgs e)
{
    var employees = new List
    {
        new Employee(1, "山田 太郎"),
        new Employee(2, "佐藤 花子")
    };

    comboBox1.DataSource = employees;
    comboBox1.ValueMember = nameof(Employee.Empno);
    comboBox1.DisplayMember = nameof(Employee.Name);
}

 

文字列ではなくnameofを使う

2013年の記事では次のように書いていました。

comboBox1.ValueMember = "Empno";
comboBox1.DisplayMember = "Name";

2026年に書くなら、私は`nameof`を使う形をおすすめします。

comboBox1.ValueMember = nameof(Employee.Empno);
comboBox1.DisplayMember = nameof(Employee.Name);

これなら、プロパティ名をリファクタリングしたときに文字列だけが古いまま残る事故を減らせます。

たとえば`Name`を`FullName`へ変更した場合、

nameof(Employee.Name)

はコンパイル時に問題へ気づけます。

しかし、

"Name"

は単なる文字列なので、コンパイラは間違いを教えてくれません。

なぜ直るのか

ComboBoxに`List`を渡したとき、Windows Formsはリスト内の要素を調べます。

そのうえで、

DisplayMember = "Name"

なら`Name`プロパティ、

ValueMember = "Empno"

なら`Empno`プロパティ、

という対応を作ります。

イメージとしては、ComboBoxがデータを受け取るときに、

> 「表示用の引き出しはどれ?」
> 「内部値の引き出しはどれ?」

と探しているようなものです。

publicフィールドは見た目こそ似ていますが、このバインディングの「引き出し一覧」に同じ形では並びません。

だからプロパティへ変更すると、ComboBoxが正しく値を取り出せるようになります。

ハマりポイント

1. publicなら何でもいいわけではない

ここが今回の中心です。

public string Name;

public string Name { get; set; }

は別物です。

データバインディングでは、まずプロパティを疑ってみると切り分けが早くなります。

2. プロパティ名のスペルミス

次のようなミスもありがちです。

comboBox1.DisplayMember = "Nmae";

`nameof`を使えば、このタイプのミスをかなり減らせます。

3. DataSourceへ何を渡しているか確認する

`List`なのか、`DataTable`なのか、`BindingSource`なのかで見え方は変わります。

今回の記事は`List`を使った検証です。

 4. 「古い記事だから間違い」とは限らない

今回ここを再検証する理由でもあります。

2013年の記事は古いですが、扱っているのはWindows Formsのデータバインディングという長く使われている仕組みです。

古い記事を削除するより、

> 「今の.NETでも本当に同じ?」

と確かめ直す方が、資料としてずっと面白くなります。

公式情報

Microsoft Learnでは、Windows FormsのComboBox / ListBoxを`IList`へバインドするとき、`DisplayMember`にはリスト内の型のpublicプロパティを指定する方法が説明されています。

– Microsoft Learn: How to bind a Windows Forms ComboBox or ListBox control to data
– Microsoft Learn: ListControl.DataSource
– Microsoft Learn: ListControl.ValueMember

HIROのひとこと

2013年の記事を見返していて、「これ、今でも同じなのかな?」と思ったのが今回の再検証のきっかけです。

APIの説明だけならAIに聞けばすぐ答えが返ってきます。

でも、13年前に実際にハマったコードを今の.NETへ持ってきて、同じところで転ぶのか確かめるのは少し違います。

古いコードは、捨てるものではなく比較用のタイムカプセルなのかもしれません。

 

まとめ

– `DisplayMember`と`ValueMember`ではデータソース側のプロパティを指定する
– publicフィールドとpublicプロパティは別物
– 2026年に書くなら`nameof`を使うと変更に強くなる
– 古い記事でも、現在環境で再実行すると新しい価値が生まれる

 

関連記事

– HIROs.NET Blog「[ハマリ] ComboBoxのDisplayMemberとValueMember」(2013年)
– HIROs.NET Blog「DataSource・DisplayMember・ValueMemberプロパティでデータバインドを行う」(2025年

Please follow and like us:

コメント

タイトルとURLをコピーしました