C# WPF ComboBoxの使い方を完全解説|データバインド・項目追加・選択値取得まで

はじめに

C#でWPFアプリケーションを開発する際、ユーザーに複数の選択肢から1つを選んでもらう場面はよくあります。そのようなときに使われる代表的なコントロールがComboBoxです。

WPFのComboBoxは、単に項目を表示して選択するだけでなく、データバインド、MVVM、ItemTemplateによる表示カスタマイズ、SelectedValueによるID取得など、実務でよく使う機能が豊富に用意されています。

一方で、SelectedItemSelectedValueSelectedIndexの違いが分かりにくかったり、初期選択が反映されなかったり、ItemsSourceを設定したのに項目が表示されなかったりと、初心者がつまずきやすいポイントもあります。

この記事では、C# WPF ComboBoxの基本的な使い方から、項目追加、データバインド、選択値の取得、MVVMでの実装、よくあるエラーの対処法まで、実用的なサンプルコードを交えて解説します。

1. C# WPF ComboBoxの基本

1-1. ComboBoxとは

ComboBoxとは、複数の候補の中からユーザーに1つの項目を選択させるためのUIコントロールです。

通常は1つの選択項目だけが表示されており、クリックするとドロップダウンリストが開いて、候補一覧から項目を選択できます。

たとえば、以下のような場面で使われます。

XML
<ComboBox>
<ComboBoxItem Content="東京" />
<ComboBoxItem Content="大阪" />
<ComboBoxItem Content="名古屋" />
</ComboBox>

このようにComboBoxを使うと、テキストボックスに自由入力させるよりも、入力ミスを防ぎやすくなります。

1-2. WPFにおけるComboBoxの役割

WPFにおけるComboBoxは、ユーザーインターフェース上で選択肢を提供するための重要なコントロールです。

WPFでは、ComboBoxを単独で使うだけでなく、データバインドと組み合わせて使うケースが非常に多くあります。

たとえば、データベースから取得したマスターデータをComboBoxに表示し、選択されたIDを保存するといった実装です。

XML
<ComboBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedCategoryId}" />

このように、WPFのComboBoxは画面表示とデータをつなぐ役割を持っています。

1-3. Windows FormsのComboBoxとの違い

C#にはWindows FormsにもComboBoxがありますが、WPFのComboBoxとは考え方が少し異なります。

Windows Formsでは、コードビハインドで項目を追加したり、イベントで処理を書いたりする方法がよく使われます。

一方、WPFではXAMLとデータバインドを使って、画面とデータを分離して実装するのが一般的です。

Windows Formsの場合は次のように書くことが多いです。

C#
comboBox1.Items.Add("東京");
comboBox1.Items.Add("大阪");
comboBox1.Items.Add("名古屋");

WPFでもコードビハインドで項目追加はできますが、実務ではItemsSourceを使ったデータバインドがよく使われます。

XML
<ComboBox ItemsSource="{Binding Prefectures}" />

WPFのComboBoxは、MVVMパターンと相性がよい点が大きな特徴です。

1-4. ComboBoxを使う主な場面

ComboBoxは、あらかじめ決まった選択肢から値を選ばせたい場合に使います。

代表的な使用例は次のとおりです。

・都道府県の選択
・カテゴリの選択
・ユーザー権限の選択
・ステータスの選択
・検索条件の選択
・年月や日付の選択
・商品分類の選択

たとえば、注文管理システムで注文ステータスを選択する場合、ComboBoxを使うことで「未処理」「処理中」「完了」「キャンセル」などの決まった値だけを選ばせることができます。

自由入力ではなく選択式にすることで、入力ミスや表記ゆれを防げる点が大きなメリットです。

2. WPF ComboBoxの基本的な使い方

2-1. XAMLでComboBoxを配置する方法

WPFでComboBoxを配置するには、XAMLにComboBoxタグを記述します。

XML
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ComboBox Sample"
Height="200"
Width="300">
<Grid>
<ComboBox Width="200"
Height="30"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Window>

このコードでは、画面中央に幅200、高さ30のComboBoxを配置しています。

ComboBoxは、GridStackPanelDockPanelなど、WPFの各種レイアウトコンテナ内に配置できます。

2-2. ComboBoxItemを使って項目を定義する

もっとも簡単な項目の追加方法は、XAML内にComboBoxItemを記述する方法です。

XML
<ComboBox Width="200">
<ComboBoxItem Content="未処理" />
<ComboBoxItem Content="処理中" />
<ComboBoxItem Content="完了" />
<ComboBoxItem Content="キャンセル" />
</ComboBox>

ComboBoxItemContentに指定した文字列が、ドロップダウンリストに表示されます。

簡単な固定リストであれば、この方法だけでも十分です。

ただし、データベースや外部ファイルから取得した値を表示したい場合は、ItemsSourceを使ったデータバインドのほうが適しています。

2-3. 初期選択項目を設定する

ComboBoxで最初から選択されている項目を指定したい場合は、SelectedIndexを使う方法があります。

XML
<ComboBox Width="200" SelectedIndex="0">
<ComboBoxItem Content="未処理" />
<ComboBoxItem Content="処理中" />
<ComboBoxItem Content="完了" />
</ComboBox>

SelectedIndex="0"を指定すると、最初の項目が初期選択されます。

インデックスは0から始まるため、2番目の項目を選択したい場合はSelectedIndex="1"を指定します。

XML
<ComboBox Width="200" SelectedIndex="1">
<ComboBoxItem Content="未処理" />
<ComboBoxItem Content="処理中" />
<ComboBoxItem Content="完了" />
</ComboBox>

ただし、MVVMやデータバインドを使う場合は、SelectedIndexよりもSelectedItemSelectedValueをバインドして初期値を設定する方法が一般的です。

2-4. IsEnabledやIsEditableなど基本プロパティの使い方

ComboBoxには、動作や見た目を制御するためのプロパティが用意されています。

IsEnabledは、ComboBoxを操作可能にするかどうかを指定します。

XML
<ComboBox Width="200" IsEnabled="False">
<ComboBoxItem Content="項目1" />
<ComboBoxItem Content="項目2" />
</ComboBox>

IsEnabled="False"にすると、ComboBoxは無効状態になり、ユーザーは選択を変更できません。

IsEditableを使うと、ユーザーがComboBoxに直接文字を入力できるようになります。

XML
<ComboBox Width="200" IsEditable="True">
<ComboBoxItem Content="東京" />
<ComboBoxItem Content="大阪" />
<ComboBoxItem Content="名古屋" />
</ComboBox>

IsEditable="True"にすると、リストから選ぶだけでなく、テキスト入力も可能になります。

また、ToolTipを使うと、マウスカーソルを乗せたときに補足説明を表示できます。

XML
<ComboBox Width="200"
ToolTip="都道府県を選択してください">
<ComboBoxItem Content="東京都" />
<ComboBoxItem Content="大阪府" />
<ComboBoxItem Content="愛知県" />
</ComboBox>

基本プロパティを組み合わせることで、用途に合わせたComboBoxを作成できます。

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

3-1. XAMLで静的に項目を追加する

固定の選択肢を表示するだけであれば、XAMLに直接項目を定義する方法が簡単です。

XML
<ComboBox Width="200">
<ComboBoxItem Content="男性" />
<ComboBoxItem Content="女性" />
<ComboBoxItem Content="回答しない" />
</ComboBox>

この方法は、項目数が少なく、アプリケーション実行中に内容が変わらない場合に向いています。

たとえば、性別、固定ステータス、検索条件などはXAMLで静的に定義してもよいでしょう。

ただし、項目数が多い場合や、データベースから取得する場合は保守性が下がるため、C#側のコレクションやViewModelからバインドする方法をおすすめします。

3-2. C#コードビハインドで項目を追加する

C#のコードビハインドからComboBoxに項目を追加することもできます。

まず、XAMLでComboBoxに名前を付けます。

XML
<ComboBox x:Name="StatusComboBox"
Width="200" />

次に、C#側でItems.Addを使って項目を追加します。

C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();

StatusComboBox.Items.Add("未処理");
StatusComboBox.Items.Add("処理中");
StatusComboBox.Items.Add("完了");
}
}

この方法はシンプルですが、項目の管理が画面側のコードに依存しやすくなります。

小さなサンプルや簡単な画面では問題ありませんが、本格的なWPFアプリケーションでは、ItemsSourceを使ったデータバインドのほうが管理しやすくなります。

3-3. Listや配列を使って項目を設定する

複数の項目をまとめて設定する場合は、List<string>や配列をItemsSourceに設定します。

XML
<ComboBox x:Name="PrefectureComboBox"
Width="200" />
C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();

var prefectures = new List<string>
{
"東京都",
"大阪府",
"愛知県",
"福岡県",
"北海道"
};

PrefectureComboBox.ItemsSource = prefectures;
}
}

配列を使う場合も同じように設定できます。

C#
string[] statuses =
{
"未処理",
"処理中",
"完了",
"キャンセル"
};

StatusComboBox.ItemsSource = statuses;

ItemsSourceを使うと、1件ずつItems.Addするよりもコードがすっきりします。

また、後からデータバインドやMVVMへ移行しやすい点もメリットです。

3-4. ObservableCollectionで項目の追加・削除を反映する

アプリケーション実行中にComboBoxの項目を追加・削除したい場合は、ObservableCollection<T>を使うのが一般的です。

ObservableCollection<T>は、コレクションの変更を画面に通知できるため、項目の追加や削除がComboBoxに自動反映されます。

C#
using System.Collections.ObjectModel;
using System.Windows;

public partial class MainWindow : Window
{
public ObservableCollection<string> Categories { get; set; }

public MainWindow()
{
InitializeComponent();

Categories = new ObservableCollection<string>
{
"食品",
"日用品",
"家電"
};

CategoryComboBox.ItemsSource = Categories;
}

private void AddButton_Click(object sender, RoutedEventArgs e)
{
Categories.Add("書籍");
}
}

XAMLは次のようになります。

XML
<StackPanel Margin="20">
<ComboBox x:Name="CategoryComboBox"
Width="200" />

<Button Content="項目追加"
Width="100"
Margin="0,10,0,0"
Click="AddButton_Click" />
</StackPanel>

List<T>の場合、要素を追加しても画面に自動反映されないことがあります。

項目の増減をリアルタイムに反映したい場合は、ObservableCollection<T>を使いましょう。

4. ComboBoxのデータバインド

4-1. ItemsSourceを使った基本的なデータバインド

WPF ComboBoxで最も重要なのが、ItemsSourceを使ったデータバインドです。

ItemsSourceには、ComboBoxに表示するデータのコレクションを指定します。

XML
<ComboBox ItemsSource="{Binding Prefectures}"
Width="200" />

ViewModel側では、次のようにプロパティを用意します。

C#
using System.Collections.ObjectModel;

public class MainViewModel
{
public ObservableCollection<string> Prefectures { get; set; }

public MainViewModel()
{
Prefectures = new ObservableCollection<string>
{
"東京都",
"大阪府",
"愛知県",
"福岡県"
};
}
}

Window側でViewModelをDataContextに設定します。

C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}

これで、Prefecturesの内容がComboBoxに表示されます。

4-2. DisplayMemberPathで表示名を指定する

ComboBoxにクラスのリストを表示する場合、何も指定しないとクラス名が表示されてしまうことがあります。

たとえば、次のようなクラスがあるとします。

C#
public class Category
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

ViewModelでカテゴリ一覧を用意します。

C#
public ObservableCollection<Category> Categories { get; set; }

public MainViewModel()
{
Categories = new ObservableCollection<Category>
{
new Category { Id = 1, Name = "食品" },
new Category { Id = 2, Name = "日用品" },
new Category { Id = 3, Name = "家電" }
};
}

XAMLでは、DisplayMemberPathに表示したいプロパティ名を指定します。

XML
<ComboBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
Width="200" />

この場合、ComboBoxには食品日用品家電が表示されます。

DisplayMemberPathは、画面に表示するプロパティを指定するための重要なプロパティです。

4-3. SelectedValuePathで選択値を指定する

SelectedValuePathを使うと、選択された項目の中から、特定のプロパティをSelectedValueとして取得できます。

たとえば、画面にはカテゴリ名を表示し、内部的にはカテゴリIDを取得したい場合に便利です。

XML
<ComboBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedCategoryId}"
Width="200" />

ViewModel側では、選択されたIDを受け取るプロパティを用意します。

C#
public int SelectedCategoryId { get; set; }

このように設定すると、ユーザーが「日用品」を選択した場合、SelectedCategoryIdには2が入ります。

実務では、画面には名称を表示し、データ保存時にはIDを使うケースが多いため、SelectedValuePathは非常によく使われます。

4-4. SelectedItem・SelectedValue・SelectedIndexの違い

ComboBoxの選択値を扱うときに混乱しやすいのが、SelectedItemSelectedValueSelectedIndexの違いです。

SelectedItemは、選択された項目そのものを取得します。

C#
var selectedCategory = CategoryComboBox.SelectedItem as Category;

SelectedValueは、SelectedValuePathで指定したプロパティの値を取得します。

C#
var selectedId = CategoryComboBox.SelectedValue;

SelectedIndexは、選択された項目の位置を0始まりの数値で取得します。

C#
int index = CategoryComboBox.SelectedIndex;

たとえば、次のようなComboBoxがあるとします。

XML
<ComboBox x:Name="CategoryComboBox"
ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id" />

この場合、選択内容ごとの取得結果は次のようになります。

SelectedItem  : Categoryオブジェクト全体
SelectedValue : Category.Idの値
SelectedIndex : リスト内の位置

オブジェクト全体が必要な場合はSelectedItem、IDだけが必要な場合はSelectedValue、表示順の位置が必要な場合はSelectedIndexを使うと覚えておくとよいでしょう。

4-5. MVVMパターンでComboBoxをバインドする

WPFでは、MVVMパターンを使ってComboBoxを実装することがよくあります。

MVVMでは、画面側のコードビハインドに処理を書くのではなく、ViewModelにデータや選択状態を持たせます。

まず、モデルクラスを作成します。

C#
public class Department
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

ViewModelを作成します。

C#
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Department> Departments { get; set; }

private int _selectedDepartmentId;
public int SelectedDepartmentId
{
get => _selectedDepartmentId;
set
{
if (_selectedDepartmentId != value)
{
_selectedDepartmentId = value;
OnPropertyChanged();
}
}
}

public MainViewModel()
{
Departments = new ObservableCollection<Department>
{
new Department { Id = 1, Name = "営業部" },
new Department { Id = 2, Name = "開発部" },
new Department { Id = 3, Name = "総務部" }
};

SelectedDepartmentId = 2;
}

public event PropertyChangedEventHandler? PropertyChanged;

private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

XAMLでは次のようにバインドします。

XML
<ComboBox ItemsSource="{Binding Departments}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedDepartmentId, Mode=TwoWay}"
Width="200" />

Window側でDataContextを設定します。

C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}

この構成にすると、画面の選択変更がViewModelのSelectedDepartmentIdに反映されます。

5. ComboBoxの選択値を取得する方法

5-1. SelectedItemで選択されたオブジェクトを取得する

SelectedItemを使うと、ComboBoxで選択された項目そのものを取得できます。

文字列のリストを使っている場合は、選択された文字列を取得できます。

C#
string? selectedText = PrefectureComboBox.SelectedItem as string;

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

クラスのリストをバインドしている場合は、選択されたオブジェクトを取得できます。

C#
var selectedCategory = CategoryComboBox.SelectedItem as Category;

if (selectedCategory != null)
{
MessageBox.Show($"ID: {selectedCategory.Id}, 名前: {selectedCategory.Name}");
}

SelectedItemは、選択されたデータ全体を扱いたい場合に適しています。

5-2. SelectedValueでIDなどの値を取得する

SelectedValueを使うと、SelectedValuePathで指定したプロパティの値を取得できます。

XML
<ComboBox x:Name="CategoryComboBox"
ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
Width="200" />

C#側では、次のように選択されたIDを取得できます。

C#
if (CategoryComboBox.SelectedValue != null)
{
int selectedId = (int)CategoryComboBox.SelectedValue;
MessageBox.Show($"選択されたID: {selectedId}");
}

画面には名前を表示し、処理ではIDを使いたい場合にSelectedValueは便利です。

データベースの主キーを扱う場合も、SelectedValueを使うと実装しやすくなります。

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

SelectedIndexを使うと、選択された項目の位置を取得できます。

C#
int index = StatusComboBox.SelectedIndex;

if (index >= 0)
{
MessageBox.Show($"選択位置: {index}");
}

SelectedIndexは0から始まります。

たとえば、1番目の項目なら0、2番目の項目なら1、3番目の項目なら2です。

何も選択されていない場合、SelectedIndex-1になります。

そのため、SelectedIndexを使う場合は、-1かどうかを確認すると安全です。

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

5-4. SelectionChangedイベントで選択変更を検知する

ComboBoxの選択が変更されたタイミングで処理を実行したい場合は、SelectionChangedイベントを使います。

XML
<ComboBox x:Name="StatusComboBox"
Width="200"
SelectionChanged="StatusComboBox_SelectionChanged">
<ComboBoxItem Content="未処理" />
<ComboBoxItem Content="処理中" />
<ComboBoxItem Content="完了" />
</ComboBox>

C#側でイベントハンドラーを実装します。

C#
private void StatusComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = sender as ComboBox;

if (comboBox?.SelectedItem is ComboBoxItem item)
{
MessageBox.Show(item.Content.ToString());
}
}

文字列リストをItemsSourceに設定している場合は、次のように取得できます。

C#
private void PrefectureComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (PrefectureComboBox.SelectedItem is string prefecture)
{
MessageBox.Show($"選択された都道府県: {prefecture}");
}
}

SelectionChangedは便利ですが、初期化時にも発火することがあります。

そのため、必要に応じてnullチェックや初期化フラグを使うと安全です。

5-5. nullチェックが必要なケース

ComboBoxの選択値を取得するときは、nullチェックが重要です。

何も選択されていない状態では、SelectedItemSelectedValueがnullになることがあります。

C#
if (CategoryComboBox.SelectedItem == null)
{
MessageBox.Show("カテゴリを選択してください。");
return;
}

SelectedValueをintにキャストする場合も、nullのままキャストすると例外が発生する可能性があります。

C#
if (CategoryComboBox.SelectedValue is int categoryId)
{
MessageBox.Show($"カテゴリID: {categoryId}");
}
else
{
MessageBox.Show("カテゴリが選択されていません。");
}

特に以下のようなケースではnullチェックが必要です。

・画面を開いた直後
・ItemsSourceがまだ設定されていない
・選択項目をクリアした
・データバインドに失敗している
・SelectedValuePathの指定が間違っている

ComboBoxの値を扱うときは、選択されている前提でコードを書かず、未選択状態も考慮しましょう。

6. ComboBoxでよく使う実装パターン

6-1. クラスのリストをComboBoxに表示する

実務では、文字列だけでなく、IDや名称を持つクラスのリストをComboBoxに表示することが多くあります。

たとえば、次のような商品カテゴリクラスを用意します。

C#
public class ProductCategory
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

ViewModelでリストを作成します。

C#
public ObservableCollection<ProductCategory> ProductCategories { get; set; }

public MainViewModel()
{
ProductCategories = new ObservableCollection<ProductCategory>
{
new ProductCategory { Id = 1, Name = "食品" },
new ProductCategory { Id = 2, Name = "日用品" },
new ProductCategory { Id = 3, Name = "家電" }
};
}

XAMLでは、表示名と選択値を指定します。

XML
<ComboBox ItemsSource="{Binding ProductCategories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedProductCategoryId}"
Width="200" />

このようにすることで、画面にはカテゴリ名を表示し、処理ではIDを扱えます。

6-2. DictionaryをComboBoxにバインドする

Dictionary<TKey, TValue>をComboBoxにバインドすることもできます。

C#
public Dictionary<int, string> StatusList { get; set; }

public MainViewModel()
{
StatusList = new Dictionary<int, string>
{
{ 1, "未処理" },
{ 2, "処理中" },
{ 3, "完了" }
};
}

XAMLでは、KeyValueを使います。

XML
<ComboBox ItemsSource="{Binding StatusList}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding SelectedStatusId}"
Width="200" />

この場合、ComboBoxにはValueである「未処理」「処理中」「完了」が表示され、選択値としてKeyであるIDを取得できます。

ただし、実務ではDictionaryよりも専用のクラスを作ったほうが、後からプロパティを追加しやすくなります。

C#
public class StatusItem
{
public int Id { get; set; }
public string Name { get; set; } = "";
public bool IsActive { get; set; }
}

単純なキーと値だけならDictionary、拡張性を重視するならクラスのリストを使うとよいでしょう。

6-3. enumをComboBoxに表示する

enumをComboBoxに表示したい場合は、Enum.GetValuesを使う方法があります。

C#
public enum OrderStatus
{
None,
Pending,
Processing,
Completed,
Canceled
}

コードビハインドで設定する場合は次のように書けます。

C#
StatusComboBox.ItemsSource = Enum.GetValues(typeof(OrderStatus));

選択されたenum値を取得する場合は、次のようにします。

C#
if (StatusComboBox.SelectedItem is OrderStatus status)
{
MessageBox.Show(status.ToString());
}

MVVMで使う場合は、ViewModelにプロパティを用意します。

C#
public Array OrderStatuses { get; } = Enum.GetValues(typeof(OrderStatus));

private OrderStatus _selectedOrderStatus;
public OrderStatus SelectedOrderStatus
{
get => _selectedOrderStatus;
set
{
if (_selectedOrderStatus != value)
{
_selectedOrderStatus = value;
OnPropertyChanged();
}
}
}

XAMLでは次のようにバインドします。

XML
<ComboBox ItemsSource="{Binding OrderStatuses}"
SelectedItem="{Binding SelectedOrderStatus, Mode=TwoWay}"
Width="200" />

enumをそのまま表示すると英語名や定義名が表示されるため、日本語表示にしたい場合は変換処理や表示用クラスを用意するとよいでしょう。

6-4. 初期値をViewModelから設定する

ComboBoxの初期選択をMVVMで設定する場合は、ViewModel側の選択プロパティに初期値を入れます。

C#
public ObservableCollection<Category> Categories { get; set; }

private int _selectedCategoryId;
public int SelectedCategoryId
{
get => _selectedCategoryId;
set
{
if (_selectedCategoryId != value)
{
_selectedCategoryId = value;
OnPropertyChanged();
}
}
}

public MainViewModel()
{
Categories = new ObservableCollection<Category>
{
new Category { Id = 1, Name = "食品" },
new Category { Id = 2, Name = "日用品" },
new Category { Id = 3, Name = "家電" }
};

SelectedCategoryId = 2;
}

XAMLは次のようになります。

XML
<ComboBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedCategoryId, Mode=TwoWay}"
Width="200" />

この場合、画面表示時にIDが2の「日用品」が初期選択されます。

初期選択が反映されない場合は、ItemsSourceのデータが設定される前に選択値を設定していないか、SelectedValuePathが正しいかを確認しましょう。

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

ComboBoxで選択された内容に応じて、TextBlockやTextBoxなど別のコントロールを更新したい場合があります。

コードビハインドで行う場合は、SelectionChangedイベントを使います。

XML
<StackPanel Margin="20">
<ComboBox x:Name="PlanComboBox"
Width="200"
SelectionChanged="PlanComboBox_SelectionChanged">
<ComboBoxItem Content="無料プラン" />
<ComboBoxItem Content="標準プラン" />
<ComboBoxItem Content="プレミアムプラン" />
</ComboBox>

<TextBlock x:Name="DescriptionTextBlock"
Margin="0,10,0,0" />
</StackPanel>
C#
private void PlanComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (PlanComboBox.SelectedItem is ComboBoxItem item)
{
string plan = item.Content.ToString() ?? "";

DescriptionTextBlock.Text = plan switch
{
"無料プラン" => "基本機能のみ利用できます。",
"標準プラン" => "一般的な機能を利用できます。",
"プレミアムプラン" => "すべての機能を利用できます。",
_ => ""
};
}
}

MVVMで行う場合は、選択プロパティのsetter内で関連プロパティを更新します。

C#
private string _selectedPlan = "";
public string SelectedPlan
{
get => _selectedPlan;
set
{
if (_selectedPlan != value)
{
_selectedPlan = value;
OnPropertyChanged();
OnPropertyChanged(nameof(PlanDescription));
}
}
}

public string PlanDescription => SelectedPlan switch
{
"無料プラン" => "基本機能のみ利用できます。",
"標準プラン" => "一般的な機能を利用できます。",
"プレミアムプラン" => "すべての機能を利用できます。",
_ => ""
};

XAMLでは、ComboBoxとTextBlockをそれぞれバインドします。

XML
<StackPanel Margin="20">
<ComboBox ItemsSource="{Binding Plans}"
SelectedItem="{Binding SelectedPlan, Mode=TwoWay}"
Width="200" />

<TextBlock Text="{Binding PlanDescription}"
Margin="0,10,0,0" />
</StackPanel>

このように、選択内容に応じて画面表示を変える処理もComboBoxではよく使われます。

7. WPF ComboBoxのカスタマイズ

7-1. ItemTemplateで表示内容をカスタマイズする

DisplayMemberPathでは1つのプロパティしか表示できません。

複数の情報を組み合わせて表示したい場合や、見た目を細かく制御したい場合は、ItemTemplateを使います。

XML
<ComboBox ItemsSource="{Binding Employees}"
Width="250">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}" />
<TextBlock Text=" - " />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

この例では、社員IDと社員名を横並びで表示しています。

対応するクラスは次のようになります。

C#
public class Employee
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

ItemTemplateを使うと、文字色、余白、画像、複数行表示なども柔軟に設定できます。

7-2. 複数項目を組み合わせて表示する

ComboBoxに「コード + 名称」のような形式で表示したい場合もあります。

たとえば、商品コードと商品名を表示する場合です。

C#
public class Product
{
public string Code { get; set; } = "";
public string Name { get; set; } = "";
}

ViewModelで商品リストを用意します。

C#
public ObservableCollection<Product> Products { get; set; }

public MainViewModel()
{
Products = new ObservableCollection<Product>
{
new Product { Code = "P001", Name = "ノートPC" },
new Product { Code = "P002", Name = "マウス" },
new Product { Code = "P003", Name = "キーボード" }
};
}

XAMLではItemTemplateを使って表示を組み立てます。

XML
<ComboBox ItemsSource="{Binding Products}"
Width="250">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Code}" />
<Run Text=" : " />
<Run Text="{Binding Name}" />
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

画面には次のように表示されます。

P001 : ノートPC
P002 : マウス
P003 : キーボード

複数のプロパティを見やすく表示したい場合は、DisplayMemberPathではなくItemTemplateを使いましょう。

7-3. ComboBoxの幅・高さ・余白を調整する

ComboBoxのサイズや余白は、WidthHeightMarginなどのプロパティで調整できます。

XML
<ComboBox Width="200"
Height="32"
Margin="10" />

左右や上下で余白を変えたい場合は、Marginに4つの値を指定します。

XML
<ComboBox Width="200"
Height="32"
Margin="10,5,10,5" />

指定順は、左、上、右、下です。

Margin="左,上,右,下"

親要素がStackPanelの場合は、次のように複数のコントロールを縦に並べられます。

XML
<StackPanel Margin="20">
<TextBlock Text="カテゴリ"
Margin="0,0,0,5" />

<ComboBox Width="200"
Height="32"
HorizontalAlignment="Left" />
</StackPanel>

HorizontalAlignmentLeftにすると、StackPanel内で左寄せにできます。

XML
<ComboBox Width="200"
HorizontalAlignment="Left" />

画面レイアウトを整える際は、ComboBox単体のサイズだけでなく、親コンテナとの関係も意識しましょう。

7-4. Styleで見た目を変更する

複数のComboBoxに同じ見た目を適用したい場合は、Styleを使います。

XML
<Window.Resources>
<Style x:Key="CommonComboBoxStyle" TargetType="ComboBox">
<Setter Property="Width" Value="220" />
<Setter Property="Height" Value="32" />
<Setter Property="Margin" Value="0,0,0,10" />
<Setter Property="FontSize" Value="14" />
</Style>
</Window.Resources>

作成したStyleは、ComboBoxに適用できます。

XML
<ComboBox Style="{StaticResource CommonComboBoxStyle}"
ItemsSource="{Binding Categories}" />

画面内のすべてのComboBoxに共通スタイルを適用したい場合は、x:Keyを省略します。

XML
<Window.Resources>
<Style TargetType="ComboBox">
<Setter Property="Width" Value="220" />
<Setter Property="Height" Value="32" />
<Setter Property="Margin" Value="0,0,0,10" />
</Style>
</Window.Resources>

この場合、そのWindow内のComboBoxに自動でスタイルが適用されます。

Styleを使うことで、画面全体のデザインを統一しやすくなります。

7-5. 編集可能なComboBoxを作る

ComboBoxを編集可能にしたい場合は、IsEditable="True"を指定します。

XML
<ComboBox Width="200"
IsEditable="True">
<ComboBoxItem Content="東京都" />
<ComboBoxItem Content="大阪府" />
<ComboBoxItem Content="愛知県" />
</ComboBox>

これにより、ユーザーはリストから選択するだけでなく、任意の文字列を入力できます。

入力されたテキストを取得したい場合は、Textプロパティを使います。

XML
<ComboBox x:Name="EditableComboBox"
Width="200"
IsEditable="True">
<ComboBoxItem Content="東京都" />
<ComboBoxItem Content="大阪府" />
<ComboBoxItem Content="愛知県" />
</ComboBox>
C#
string inputText = EditableComboBox.Text;
MessageBox.Show(inputText);

IsEditableは、候補を表示しつつ自由入力も許可したい場合に便利です。

ただし、ユーザーが候補にない値を入力できるため、入力チェックが必要になることがあります。

8. ComboBoxでよくあるエラーと対処法

8-1. 項目が表示されない原因

ComboBoxに項目が表示されない場合、まず確認すべきなのはItemsSourceです。

よくある原因は次のとおりです。

・DataContextが設定されていない
・ItemsSourceのプロパティ名が間違っている
・プロパティがpublicになっていない
・コレクションがnullになっている
・DisplayMemberPathのプロパティ名が間違っている

たとえば、XAMLで次のように書いているとします。

XML
<ComboBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name" />

ViewModel側にCategoriesプロパティが存在しない場合、項目は表示されません。

C#
public ObservableCollection<Category> CategoryList { get; set; }

この場合、XAMLをCategoryListに合わせる必要があります。

XML
<ComboBox ItemsSource="{Binding CategoryList}"
DisplayMemberPath="Name" />

また、DataContextの設定漏れもよくある原因です。

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

まずは、プロパティ名、DataContext、コレクションの中身を確認しましょう。

8-2. SelectedValueが取得できない原因

SelectedValueがnullになる場合や期待した値にならない場合は、SelectedValuePathを確認します。

XML
<ComboBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedCategoryId}" />

この例では、CategoryクラスにIdプロパティが必要です。

C#
public class Category
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

もしSelectedValuePath="CategoryId"と書いているのに、クラス側のプロパティがIdであれば、正しく取得できません。

XML
<ComboBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="CategoryId" />

この場合は、次のように修正します。

XML
<ComboBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id" />

SelectedValueが取得できないときは、表示名ではなく、値として取得したいプロパティ名が正しく指定されているかを確認しましょう。

8-3. 初期選択が反映されない原因

ComboBoxの初期選択が反映されない場合、主な原因は次のとおりです。

・SelectedValueに設定した値がItemsSource内に存在しない
・SelectedValuePathが間違っている
・ItemsSource設定前に選択値を設定している
・INotifyPropertyChangedが実装されていない
・バインドモードが意図どおりではない

たとえば、次のようなカテゴリ一覧があるとします。

C#
Categories = new ObservableCollection<Category>
{
new Category { Id = 1, Name = "食品" },
new Category { Id = 2, Name = "日用品" }
};

SelectedCategoryId = 3;

この場合、Id = 3の項目が存在しないため、初期選択は反映されません。

正しく初期選択したい場合は、存在する値を設定します。

C#
SelectedCategoryId = 2;

また、ViewModelから値を後で変更する場合は、INotifyPropertyChangedを実装して変更通知を行う必要があります。

C#
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedCategoryId)));

初期選択がうまくいかない場合は、まず「選択値が一覧に存在するか」を確認しましょう。

8-4. Bindingエラーが出る原因

WPFでBindingエラーが出る場合、Visual Studioの出力ウィンドウにエラー内容が表示されます。

よくあるBindingエラーの原因は、プロパティ名の間違いです。

XML
<ComboBox ItemsSource="{Binding Categorys}" />

ViewModel側のプロパティがCategoriesの場合、Categorysというプロパティは存在しないため、Bindingエラーになります。

C#
public ObservableCollection<Category> Categories { get; set; }

正しくは次のように書きます。

XML
<ComboBox ItemsSource="{Binding Categories}" />

また、プロパティがprivateになっている場合もバインドできません。

C#
private ObservableCollection<Category> Categories { get; set; }

WPFのBinding対象は、基本的にpublicプロパティにします。

C#
public ObservableCollection<Category> Categories { get; set; }

Bindingエラーが出た場合は、Visual Studioの出力ウィンドウを確認し、プロパティ名やDataContextの設定を見直しましょう。

8-5. SelectionChangedが意図せず発火する場合の対処法

SelectionChangedイベントは、ユーザーが選択を変更したときだけでなく、初期化時やItemsSource設定時にも発火することがあります。

そのため、初期化中に処理を実行したくない場合は、フラグを使って制御します。

C#
private bool _isInitialized = false;

public MainWindow()
{
InitializeComponent();

StatusComboBox.ItemsSource = new List<string>
{
"未処理",
"処理中",
"完了"
};

_isInitialized = true;
}

private void StatusComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!_isInitialized)
{
return;
}

if (StatusComboBox.SelectedItem is string status)
{
MessageBox.Show(status);
}
}

また、SelectedItemがnullのときに処理しないようにすることも重要です。

C#
private void StatusComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (StatusComboBox.SelectedItem == null)
{
return;
}

// 選択変更時の処理
}

MVVMでは、イベントではなく選択プロパティのsetterで処理する方法もあります。

C#
private string? _selectedStatus;
public string? SelectedStatus
{
get => _selectedStatus;
set
{
if (_selectedStatus != value)
{
_selectedStatus = value;
OnPropertyChanged();

if (_selectedStatus != null)
{
// 選択変更時の処理
}
}
}
}

意図しないタイミングで処理が動く場合は、初期化中かどうか、nullかどうかを確認するようにしましょう。

9. C# WPF ComboBoxの実用サンプル

9-1. 都道府県を選択するComboBox

まずは、都道府県を選択するシンプルなComboBoxのサンプルです。

XML
<StackPanel Margin="20">
<TextBlock Text="都道府県を選択してください"
Margin="0,0,0,5" />

<ComboBox x:Name="PrefectureComboBox"
Width="200"
SelectedIndex="0">
<ComboBoxItem Content="東京都" />
<ComboBoxItem Content="大阪府" />
<ComboBoxItem Content="愛知県" />
<ComboBoxItem Content="福岡県" />
<ComboBoxItem Content="北海道" />
</ComboBox>

<Button Content="選択値を表示"
Width="120"
Margin="0,10,0,0"
Click="Button_Click" />
</StackPanel>

C#側では、選択された都道府県を取得します。

C#
private void Button_Click(object sender, RoutedEventArgs e)
{
if (PrefectureComboBox.SelectedItem is ComboBoxItem item)
{
MessageBox.Show(item.Content.ToString());
}
}

固定の項目を扱うだけであれば、このようにXAMLだけで項目を定義できます。

9-2. IDと表示名を持つデータを扱うComboBox

次に、IDと表示名を持つデータをComboBoxで扱うサンプルです。

C#
public class Category
{
public int Id { get; set; }
public string Name { get; set; } = "";
}

ViewModelを作成します。

C#
using System.Collections.ObjectModel;

public class MainViewModel
{
public ObservableCollection<Category> Categories { get; set; }

public int SelectedCategoryId { get; set; }

public MainViewModel()
{
Categories = new ObservableCollection<Category>
{
new Category { Id = 1, Name = "食品" },
new Category { Id = 2, Name = "日用品" },
new Category { Id = 3, Name = "家電" }
};

SelectedCategoryId = 1;
}
}

XAMLでは、DisplayMemberPathSelectedValuePathを設定します。

XML
<StackPanel Margin="20">
<ComboBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedCategoryId}"
Width="200" />
</StackPanel>

Window側でDataContextを設定します。

C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}

このサンプルでは、画面にはカテゴリ名を表示し、内部的にはカテゴリIDを扱えます。

9-3. MVVMで選択値を取得するサンプル

MVVMで選択値を取得する場合は、ViewModelに選択用のプロパティを用意します。

C#
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Category> Categories { get; set; }

private int _selectedCategoryId;
public int SelectedCategoryId
{
get => _selectedCategoryId;
set
{
if (_selectedCategoryId != value)
{
_selectedCategoryId = value;
OnPropertyChanged();
OnPropertyChanged(nameof(SelectedCategoryMessage));
}
}
}

public string SelectedCategoryMessage
=> $"選択されたカテゴリID: {SelectedCategoryId}";

public MainViewModel()
{
Categories = new ObservableCollection<Category>
{
new Category { Id = 1, Name = "食品" },
new Category { Id = 2, Name = "日用品" },
new Category { Id = 3, Name = "家電" }
};

SelectedCategoryId = 2;
}

public event PropertyChangedEventHandler? PropertyChanged;

private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

XAMLでは、ComboBoxとTextBlockをViewModelにバインドします。

XML
<StackPanel Margin="20">
<ComboBox ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SelectedCategoryId, Mode=TwoWay}"
Width="200" />

<TextBlock Text="{Binding SelectedCategoryMessage}"
Margin="0,10,0,0" />
</StackPanel>

この実装では、ComboBoxの選択が変わるとSelectedCategoryIdが更新され、TextBlockの表示も更新されます。

9-4. 選択内容をTextBlockに表示するサンプル

最後に、ComboBoxで選択した内容をTextBlockに表示するサンプルです。

XML
<StackPanel Margin="20">
<ComboBox x:Name="ColorComboBox"
Width="200"
SelectionChanged="ColorComboBox_SelectionChanged">
<ComboBoxItem Content="赤" />
<ComboBoxItem Content="青" />
<ComboBoxItem Content="緑" />
</ComboBox>

<TextBlock x:Name="ResultTextBlock"
Margin="0,10,0,0"
FontSize="16" />
</StackPanel>

C#側では、SelectionChangedイベントでTextBlockを更新します。

C#
private void ColorComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ColorComboBox.SelectedItem is ComboBoxItem item)
{
ResultTextBlock.Text = $"選択された色: {item.Content}";
}
}

コードビハインドで簡単に実装する場合は、この方法が分かりやすいです。

MVVMで実装する場合は、TextBlockのTextもViewModelのプロパティにバインドすると、画面とロジックを分離できます。

XML
<TextBlock Text="{Binding SelectedColorText}" />
C#
public string SelectedColorText => $"選択された色: {SelectedColor}";

簡単な画面ではコードビハインド、本格的なアプリではMVVMと使い分けるとよいでしょう。

まとめ

C# WPFのComboBoxは、複数の選択肢から1つを選ばせるための基本的なコントロールです。

固定の項目を表示するだけであれば、XAMLにComboBoxItemを直接記述するだけで実装できます。

一方、実務では、ItemsSourceを使ってリストやクラスのコレクションをバインドし、DisplayMemberPathで表示名を指定し、SelectedValuePathでIDなどの値を取得する実装がよく使われます。

選択値を取得する方法には、主に次の3つがあります。

SelectedItem  : 選択されたオブジェクト全体を取得する
SelectedValue : SelectedValuePathで指定した値を取得する
SelectedIndex : 選択された位置を取得する

画面には名称を表示し、内部処理ではIDを扱いたい場合は、DisplayMemberPathSelectedValuePathの組み合わせが便利です。

また、WPFではMVVMパターンと組み合わせることで、画面とロジックを分離し、保守しやすいコードにできます。

ComboBoxで項目が表示されない、初期選択が反映されない、SelectedValueが取得できないといった問題が起きた場合は、DataContextItemsSourceDisplayMemberPathSelectedValuePath、選択値の存在を順番に確認しましょう。

C# WPFでComboBoxを正しく使えるようになると、入力フォーム、検索条件、設定画面、マスターデータ選択など、さまざまな画面を効率よく実装できるようになります。