C#WPFの道#20!ComboBoxの書き方と使い方を解りやすく解説

当サイトではアフィリエイト広告を利用しています。

WPF

ComboBox(コンボボックス)とは?

ComboBoxとは,一覧の中から,任意の項目を選択するときに使うコントロールです。

主な使い方

使い方は,単純にItemsに文字をAddして使うやり方もできますが,実際のプログラミングでは,表示されている文字とは別に,内部ではIdで扱うことがほとんどなので,基本的にはItemsSourceにデータバインディングをして使うことになります。今回はそれぞれのやり方を見ていきます。

パターン1:単純にItemsに文字をAddして使う

<StackPanel Orientation="Horizontal">
    <ComboBox x:Name="MyComboBox"
              Height="40"
              Width="200"
              Margin="10"
              FontSize="20"
              VerticalAlignment="Center"/>
    <Button Margin="10"
            Width="50"
            Content="check"
            Click="MyButton_Click"
            />
</StackPanel>
  • Xamlにコンボボックスとボタンを設置
public MainWindow()
{
    InitializeComponent();

    MyComboBox.Items.Add("111");
    MyComboBox.Items.Add("222");
    MyComboBox.Items.Add("333");
}

private void MyButton_Click(object sender, RoutedEventArgs e)
{
    var sb = new StringBuilder();
    sb.AppendLine("MyComboBox.SelectedIndex:" + MyComboBox.SelectedIndex);
    sb.AppendLine("MyComboBox.SelectedValue:" + MyComboBox.SelectedValue);
    sb.AppendLine("MyComboBox.Text:" + MyComboBox.Text);
    MessageBox.Show(sb.ToString());
}
  • コンストラクタでコンボボックスに文字列を3つ追加
  • ボタンクリックイベントで各プロパティの内容を確認するメッセージボックスを表示

実行結果

  • シンプルに文字列が3つ追加されている。

「222」を選択してCheckボタンを押下したときのメッセージボックス

  • SelectedIndexは「1」。これはインデックスが0始まりなので2番目を選択したためインデックスは1となる。
  • SelectedValueとTextは両方「222」となる。

この方法は非常にシンプルだが,任意の文字列を表示して,内部ではIDで管理するというコーディングができない。実際のプログラミングでは使用することはないと思います。私は基本的に使いません。プロトタイプで取り急ぎ表示させる時くらいにしか用途はないかと思います。

パターン2:データバインディング(単一項目表示)

ここからはデータバインディングする方法を見ていきたいと思います。データバインディングする場合は,任意のクラスを作成して,そのリストをコンボボックスのItemsSourceに設定します。Idと表示文字をセットにして,コンボボックスで扱うだけであれば,SelectedValuePathとDisplayMemberPathを使用することで実現できます。

  • ItemsSource
    • コンボボックスにバインディングするリスト
  • SelectedValuePath
    • 選択されているときにSelectedValueで取得される項目を設定
  • DisplayMemberPath
    • 表示する文字の項目を設定。Textで取得される値。
namespace WPF020
{
    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Phone { get; set; }
    }
}
  • コンボボックスにデータバインディングするためのクラスです。IDと名前と電話番号のクラスです。
<StackPanel Orientation="Horizontal">
    <ComboBox x:Name="AComboBox"
              Height="40"
              Width="200"
              Margin="10"
              FontSize="20"
              VerticalAlignment="Center"
              SelectedValuePath="Id"
              DisplayMemberPath="Name"/>
    <Button Margin="10"
            Width="50"
            Content="check"
            Click="AButton_Click"
            />
</StackPanel>
  • SelectedValuePathとDisplayMemberPathを設定しています。
  • 表示文字はNameを表示し,内部的にはIdで処理をすることができます。
  • 選択されているIdはSelectedValueで取得できます。
  • 選択されているNameはTextで取得することができます。
private ObservableCollection<Customer> _customers = new ObservableCollection<Customer>();
public MainWindow()
{
    InitializeComponent();

    _customers.Add(new Customer { Id = 1, Name = "name1", Phone = "Phone1" });
    _customers.Add(new Customer { Id = 2, Name = "name2", Phone = "Phone2" });
    _customers.Add(new Customer { Id = 3, Name = "name3", Phone = "Phone3" });

    AComboBox.ItemsSource = _customers;
}

private void AButton_Click(object sender, RoutedEventArgs e)
{
    var item = AComboBox.SelectedItem as Customer;
    if (item != null)
    {
        var sb = new StringBuilder();
        sb.AppendLine("AComboBox.SelectedIndex:" + AComboBox.SelectedIndex);
        sb.AppendLine("AComboBox.SelectedValue:" + AComboBox.SelectedValue);
        sb.AppendLine("AComboBox.Text:" + AComboBox.Text);
        sb.AppendLine("-------");
        sb.AppendLine("SelectedItem.Id : " + item.Id);
        sb.AppendLine("SelectedItem.Name : " + item.Name);
        sb.AppendLine("SelectedItem.Phone : " + item.Phone);
        MessageBox.Show(sb.ToString());
    }
}
  • PrivateフィールドにObservableCollectionでCustomerのリストを宣言しています。
  • コンストラクタで3つのCustomerを生成して_customersにAddしています。
  • ボタンクリック時に各プロパティの値を確認しています。
  • SelectedItemはObject型の為Customerクラスに変換し,取得できた場合のみ処理しています。

実行結果

  • ObservableCollectionに追加したCustomerクラスのNameの値がリストされています。これはDisplayMemberPathに「Name」を設定しているためです。

「name2」を選択して「check」ボタンを押下したとき

  • SelectedIndexには「1」が設定されています。
  • SelectedValueには「2」が設定されています。これはSelectedValuePathで「Id」を設定しているためです。
  • Textには「name2」が設定されています。これはDisplayMemberPathに「Name」を設定しているためです。
  • SelectedItemには選択されているCustomerの値が取得できています。

この方法を使えば,任意の表示項目と,内部で扱うIdを分けてコーディングすることができます。ただ,表示する項目を加工したい場合や,複数のコントロールに分けて表示するレイアウトにしたい場合は実現できないので,次に紹介するItemTemplateとDataTemplateを使用するやり方で実現します。

パターン3:リストに複数のコントロールを表示する場合

リストに複数のコントロールを置く場合はItemTemplateとDataTemplateを使って実現できます。

<StackPanel Orientation="Horizontal">
    <ComboBox x:Name="BComboBox"
              Height="40"
              Width="200"
              Margin="10"
              FontSize="20"
              VerticalAlignment="Center">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Id}" FontSize="20" Margin="5"/>
                    <TextBlock Text="{Binding Name}" FontSize="20" Margin="5"/>
                    <TextBlock Text="{Binding Phone}" FontSize="20" Margin="5"/>
                </StackPanel>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>
    <Button Margin="10"
            Width="50"
            Content="check"
            Click="BButton_Click"
            />
</StackPanel>
  • ItemTemplateとDataTemplateエリアを記述します。
  • DataTemplateの中に任意のコントロールを設置します。ここではStackPanelの中に複数のTextBlockを設置しています。
  • TextBlockのTextにはItemsSourceにデータバインディングする値に対応するプロパティをバインドしています。
private ObservableCollection<Customer> _customers = new ObservableCollection<Customer>();
public MainWindow()
{
    InitializeComponent();

    _customers.Add(new Customer { Id = 1, Name = "name1", Phone = "Phone1" });
    _customers.Add(new Customer { Id = 2, Name = "name2", Phone = "Phone2" });
    _customers.Add(new Customer { Id = 3, Name = "name3", Phone = "Phone3" });

    BComboBox.ItemsSource = _customers;
}

private void BButton_Click(object sender, RoutedEventArgs e)
{
    var item = BComboBox.SelectedItem as Customer;
    if (item != null)
    {
        var sb = new StringBuilder();
        sb.AppendLine("AComboBox.SelectedIndex:" + BComboBox.SelectedIndex);
        sb.AppendLine("AComboBox.SelectedValue:" + BComboBox.SelectedValue);
        sb.AppendLine("AComboBox.Text:" + BComboBox.Text);
        sb.AppendLine("-------");
        sb.AppendLine("SelectedItem.Id : " + item.Id);
        sb.AppendLine("SelectedItem.Name : " + item.Name);
        sb.AppendLine("SelectedItem.Phone : " + item.Phone);
        MessageBox.Show(sb.ToString());
    }
}

実行結果

  • 複数のTextBlockが表示されています。

2番目の値を選択して「check」を押下したとき

  • SelectedIndexには「1」が設定されています。
  • SelectedValueとTextには「Customer」という文字が表示されています。これはCustomerクラスをToString()したときの文字列です。今回はSelectedValuePathとDisplayMemberPathを設定していないためこうなっています。SelectedValuePathをIdにしておくことで,SelectedValueをIdで取得することは可能です。
  • SelectedItemには選択されているCustomerの値が取得できています。この値を常に参照するという事であれば,SelectedValuePathを設定しなくてもコーディングは可能です。

サンプルコード全体

namespace WPF020
{
    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Phone { get; set; }
    }
}
<Window x:Class="WPF020.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF020"
        mc:Ignorable="d"
        Title="MainWindow" Height="240" Width="300">
    <Grid>
        <StackPanel>
            <StackPanel Orientation="Horizontal">
                <ComboBox x:Name="MyComboBox"
                          Height="40"
                          Width="200"
                          Margin="10"
                          FontSize="20"
                          VerticalAlignment="Center"/>
                <Button Margin="10"
                        Width="50"
                        Content="check"
                        Click="MyButton_Click"
                        />

            </StackPanel>

            <StackPanel Orientation="Horizontal">
                <ComboBox x:Name="AComboBox"
                          Height="40"
                          Width="200"
                          Margin="10"
                          FontSize="20"
                          VerticalAlignment="Center"
                          SelectedValuePath="Id"
                          DisplayMemberPath="Name"/>
                <Button Margin="10"
                        Width="50"
                        Content="check"
                        Click="AButton_Click"
                        />

            </StackPanel>

            <StackPanel Orientation="Horizontal">
                <ComboBox x:Name="BComboBox"
                          Height="40"
                          Width="200"
                          Margin="10"
                          FontSize="20"
                          VerticalAlignment="Center">
                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Id}" FontSize="20" Margin="5"/>
                                <TextBlock Text="{Binding Name}" FontSize="20" Margin="5"/>
                                <TextBlock Text="{Binding Phone}" FontSize="20" Margin="5"/>
                            </StackPanel>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                </ComboBox>
                <Button Margin="10"
                        Width="50"
                        Content="check"
                        Click="BButton_Click"
                        />

            </StackPanel>
        </StackPanel>
    </Grid>
</Window>
using System.Collections.ObjectModel;
using System.Text;
using System.Windows;

namespace WPF020
{
    ///
    /// MainWindow.xaml の相互作用ロジック
    /// 
    public partial class MainWindow : Window
    {
        private ObservableCollection<Customer> _customers = new ObservableCollection<Customer>();
        public MainWindow()
        {
            InitializeComponent();

            MyComboBox.Items.Add("111");
            MyComboBox.Items.Add("222");
            MyComboBox.Items.Add("333");

            _customers.Add(new Customer { Id = 1, Name = "name1", Phone = "Phone1" });
            _customers.Add(new Customer { Id = 2, Name = "name2", Phone = "Phone2" });
            _customers.Add(new Customer { Id = 3, Name = "name3", Phone = "Phone3" });

            AComboBox.ItemsSource = _customers;
            BComboBox.ItemsSource = _customers;
        }

        private void MyButton_Click(object sender, RoutedEventArgs e)
        {
            var sb = new StringBuilder();
            sb.AppendLine("MyComboBox.SelectedIndex:" + MyComboBox.SelectedIndex);
            sb.AppendLine("MyComboBox.SelectedValue:" + MyComboBox.SelectedValue);
            sb.AppendLine("MyComboBox.Text:" + MyComboBox.Text);
            MessageBox.Show(sb.ToString());
        }

        private void AButton_Click(object sender, RoutedEventArgs e)
        {
            var item = AComboBox.SelectedItem as Customer;
            if (item != null)
            {
                var sb = new StringBuilder();
                sb.AppendLine("AComboBox.SelectedIndex:" + AComboBox.SelectedIndex);
                sb.AppendLine("AComboBox.SelectedValue:" + AComboBox.SelectedValue);
                sb.AppendLine("AComboBox.Text:" + AComboBox.Text);
                sb.AppendLine("-------");
                sb.AppendLine("SelectedItem.Id : " + item.Id);
                sb.AppendLine("SelectedItem.Name : " + item.Name);
                sb.AppendLine("SelectedItem.Phone : " + item.Phone);
                MessageBox.Show(sb.ToString());
            }
        }

        private void BButton_Click(object sender, RoutedEventArgs e)
        {
            var item = BComboBox.SelectedItem as Customer;
            if (item != null)
            {
                var sb = new StringBuilder();
                sb.AppendLine("AComboBox.SelectedIndex:" + BComboBox.SelectedIndex);
                sb.AppendLine("AComboBox.SelectedValue:" + BComboBox.SelectedValue);
                sb.AppendLine("AComboBox.Text:" + BComboBox.Text);
                sb.AppendLine("-------");
                sb.AppendLine("SelectedItem.Id : " + item.Id);
                sb.AppendLine("SelectedItem.Name : " + item.Name);
                sb.AppendLine("SelectedItem.Phone : " + item.Phone);
                MessageBox.Show(sb.ToString());
            }
        }
    }
}