C#とWPFを利用して、ComboBoxを使う方法をまとめてみました。
ComboBoxを使う方法は、いくつかあり、それぞれ使い方がビミョーに異なるなど混乱しそうです。そこで整理してみることに。いろいろな切り口で整理できそうですが、今回は「ComboBoxに表示する内容をどのように定義するか」に着目してみました。
次のように大きく3つにまとめることができるので、判りやすいと思います。
- XAMLファイルに定義するケース(Textに割り当てられるので、そのまま利用する)
- CodeBehindに定義し、Text か Value に割り当てて利用するケース
- CodeBehindに定義し、ItemTemplate を使って自由に割り当てて利用するケース
最初の方法はお手軽ですが、ほとんど柔軟性がありません。単純な内容で将来も変わらないだろうと予想できる場合などに向いています。
2番目の方法は表示できる項目が1つだけですが、内容を変更することもできるなど、ある程度柔軟性があります。
3番目の方法は、この中では一番複雑ですが、複数の項目を表示できたり、装飾もできるなど、もっとも柔軟性があります。
使用環境(MVVMは使いません)
・Window 11
・Visual Studio 2022
・.NET 7.0 (C# 11.0)
全体像
説明にあたり次のような画面を作ってみました。

まとめた3つの方法に対応して、ComboBoxを上から3つ並べます。そして、それぞれのComboBoxの内容を確認できるようにボタンを3つ配置しました。
ボタンをクリックするとすぐ上のComboBoxの内容を表示する小窓を出します。
1. XAMLファイルに定義する場合
まず1番目の方法です。ComboBoxに表示する内容を、XAMLファイルに定義します。
このケースでは、ComboBoxの定義はXAMLだけでできてしまいます。
XAMLの内容は次のようになります。
<ComboBox x:Name="ComboBox1" SelectedValuePath="Content" Margin="10,10,10,10" >
<ComboBoxItem Content="データ1行目" IsSelected="True" />
<ComboBoxItem Content="データ2行目" />
<ComboBoxItem Content="データ3行目" />
</ComboBox>
<Button Content="内容確認ボタン1" x:Name="Button1" Click="Button1_Click" Margin="10,10,10,10" />
「ComboBox」の中に「ComboBoxItem」を追記して、「Content=”~”」のように内容を定義していきます。
内容は自動で「ComboBox1.Text」に割り当てられます。なのでこの方法では「DisplayMemberPath=」を指定してはいけません。指定するとComboBoxにデータが表示されなくなります。
「IsSelected=”True” 」と指定した行は、ComboBoxに初期表示されます。複数指定してあれば最後の行が表示されます。
「SelectedValuePath=”Content” 」は指定しなくても動作します。
指定すると「ComboBox1.SelectedValue」にも内容が割り当てられます。
指定しなければ、「ComboBox1.SelectedValue」には、ComboBoxItemのオブジェクトを文字列形式に変換した値がついてきますので、そのままでは利用が難しくなります。
次にCodeBehindの内容です。
private void Button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show($"SelectedIndex={ComboBox1.SelectedIndex}\nText={ComboBox1.Text}\nSelectedValue={ComboBox1.SelectedValue}", "ComboBox1");
}
1番目の方法では、内容確認用のボタンだけを記載すればCodeBehindは完成です。
次の項目を把握しておけば、ほとんどのケースで困らないと思います。
- 「ComboBox1.SelectedIndex」には1行目から順にゼロ始まりで連番が入ります。つまり1行目ならゼロ、2行目なら1が入っています。
- 「ComboBox1.Text」は、XAMLの「Content=」で指定した値が入ります。
- 「ComboBox1.SelectedValue」には、「SelectedValuePath=”Content” 」と指定してあれば、XAMLの「Content=」で指定した値が入ります。
2. CodeBehindに定義し、Text か Value に割り当てて利用する場合
2番目の方法です。
ComboBoxに表示する内容を、CodeBehindでクラスとして定義して生成し、「ComboBox2.Text」か「ComboBox1.Value」に割り当てて利用します。
まず、XAMLファイルは次の通りになります。
<ComboBox x:Name="ComboBox2" DisplayMemberPath="Item1" SelectedValuePath="Item2" Margin="10,10,10,10" ></ComboBox>
<Button Content="内容確認ボタン2" x:Name="Button2" Click="Button2_Click" Margin="10,10,10,10" />
例にある “Item1” “Item2” というのは後で説明するCodeBehindで定義する項目名になります。
「DisplayMemberPath=」でComboBoxに表示する項目を指定します。
「SelectedValuePath=”~” 」は指定しなくても動作します。指定すると「ComboBox1.SelectedValue」に指定した項目の値が入ります。指定しなければ、「ComboBox2.SelectedValue」には、割り当てたオブジェクトを文字列形式に変換した値が入るので、使い道はないと思います。
なお、「ComboBox2.SelectedValue」の値は画面には表示されません。
次にCodeBehindの内容です。
まず、ComboBoxに表示する内容をクラスとして定義して生成する部分です。
public MainWindow()
{
InitializeComponent();
ComboBox2.ItemsSource = comboBox2List;
comboBox2List.Add(new ComboBox2List { Item1 = "1行目データ1", Item2 = "1行目データ2" });
comboBox2List.Add(new ComboBox2List { Item1 = "2行目データ1", Item2 = "2行目データ2" });
comboBox2List.Add(new ComboBox2List { Item1 = "3行目データ1", Item2 = "3行目データ2" });
ComboBox2.SelectedIndex = 0;
}
private readonly ObservableCollection<ComboBox2List> comboBox2List = new();
public class ComboBox2List
{
public string? Item1 { get; set; }
public string? Item2 { get; set; }
}
普通は、ObservableCollection クラスで定義します。配列などでもできないことはないですが、使い勝手が悪くなります。
ここでは、ComboBoxに表示する項目としてクラス「ComboBox2List」を定義します。
このケースでは、ObservableCollectionで複数の項目を定義しても、ComboBoxに表示できる項目は1つだけです。
そして、ObservableCollection の型に当てはめて、コレクションを生成します。
MainWindowのコンストラクタで、コレクションは「ComboBox2.ItemsSource」に割り当てるとともに、行を追加してコレクションを完成させます。
ComboBoxに初期表示させる行があれば「ComboBox2.SelectedIndex =」で指定します。初期表示は「ComboBox2.Text」を使ってもできますが、「ComboBox2.SelectedIndex =」の方が間違え難いのでオススメです。
そして、内容確認用のボタンについては、次のようになります。
private void Button2_Click(object sender, RoutedEventArgs e)
{
if (ComboBox2.SelectedIndex >= 0)
{
MessageBox.Show($"SelectedIndex={ComboBox2.SelectedIndex}\nText={ComboBox2.Text}\nSelectedValue={ComboBox2.SelectedValue}\n" +
$"Item1={comboBox2List[ComboBox2.SelectedIndex].Item1}\nItem2={comboBox2List[ComboBox2.SelectedIndex].Item2}", "ComboBox2");
}
}
「ComboBox2.SelectedIndex」をインデックスにして表示させるため、ComboBoxが未選択の場合は実行エラーになります。これを回避するため「ComboBox2.SelectedIndex」がゼロ以上の場合のみMessageBoxを出すようにします。
最初の行が選択されていれば、ボタンをクリックすると次のようなメッセージが表示されます。

3. CodeBehindに定義し、ItemTemplate を使って自由に割り当てて利用する場合
3番目の方法です。
ComboBoxに表示する内容を、CodeBehindでクラスとして定義して生成し、ItemTemplate を使って自由に割り当てて利用します。
まず、XAMLファイルは次の通りになります。
<ComboBox x:Name="ComboBox3" SelectedValuePath="Item2" Margin="10,10,10,10" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Item1}" Margin="0,0,0,0" />
<TextBlock Text=":" Margin="5,0,0,0" />
<TextBlock Text="{Binding Item2}" Margin="5,0,0,0" />
<TextBlock Text="★" Margin="5,0,0,0" />
<TextBlock Text="{Binding Item3}" Margin="5,0,0,0" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="内容確認ボタン3" x:Name="Button3" Click="Button3_Click" Margin="10,10,10,10" />
まず、ComboBoxの中に、ComboBox.ItemTemplate、DataTemplate、StackPanel~ を定義します。
そして「TextBlock」を必要な項目の数だけ定義し「Text=”{Binding Item1}”」などと記載して、後で説明するCodeBehindで定義する項目名に割り当てます。
上の例の「:」や「★」のように項目に割り当てず固定値にすることもできます。
このように柔軟にComboBoxに表示する内容をデザインできる点がこの方法のメリットです。
このケースでは「DisplayMemberPath=」で項目を指定できません。指定すると実行エラーになります。
「SelectedValuePath=”~” 」は指定しなくても動作します。指定すると「ComboBox3.SelectedValue」に指定した項目の値が入ります。
指定しなければ、「ComboBox3.SelectedValue」には、割り当てたオブジェクトを文字列形式に変換した値が入るので、使い道はないと思います。
次にCodeBehindの内容です。
まず、ComboBoxに表示する内容をクラスとして定義して生成する部分です。
public MainWindow()
{
InitializeComponent();
ComboBox3.ItemsSource = comboBox3List;
comboBox3List.Add(new ComboBox3List { Item1 = "1行目データ1", Item2 = "1行目データ2", Item3 = "1行目データ3" });
comboBox3List.Add(new ComboBox3List { Item1 = "2行目データ1", Item2 = "2行目データ2", Item3 = "2行目データ3" });
comboBox3List.Add(new ComboBox3List { Item1 = "3行目データ1", Item2 = "3行目データ2", Item3 = "3行目データ3" });
ComboBox3.SelectedIndex = 0;
}
private readonly ObservableCollection<ComboBox3List> comboBox3List = new();
public class ComboBox3List
{
public string? Item1 { get; set; }
public string? Item2 { get; set; }
public string? Item3 { get; set; }
}
2番目の方法とほぼ同じです。
そして、内容確認用のボタンについては、次のようになります。
private void Button3_Click(object sender, RoutedEventArgs e)
{
if (ComboBox3.SelectedIndex >= 0)
{
MessageBox.Show($"SelectedIndex={ComboBox3.SelectedIndex}\nText={ComboBox3.Text}\nSelectedValue={ComboBox3.SelectedValue}\n" +
$"Item1={comboBox3List[ComboBox3.SelectedIndex].Item1}\nItem2={comboBox3List[ComboBox3.SelectedIndex].Item2}\nItem3={comboBox3List[ComboBox3.SelectedIndex].Item3}", "ComboBox3");
}
}
2番目の方法とほぼ同じですが、「ComboBox3.Text」については、このままでは割り当てたオブジェクトを文字列形式に変換した値が入ります。
最初の行が選択されていれば、ボタンをクリックすると次のようなメッセージが表示されます。

さいごに
ComboBoxの利用方法を「ComboBoxに表示する内容をどのように定義するか」に着目して、3つに分けて説明しました。「3つもいらない。とりあえず1つだけ選びたい」というのであれば3番目の方法がオススメです。コーディングは少しだけ複雑になりますが、柔軟性がグッとアップするので、いちばんお得なのではないかと思います。
コメント