列挙体をコンボボックスにBindingする

category: WPF | 2018-04-03

マークアップ拡張を利用することで、列挙体をコンボボックスにBindingすることができます。
また、今回は列挙体に任意の文字列を紐付けて表示するようにします。

ObjectDataProviderを利用する方法もありますが、アプリ内で利用する度にObjectDataProviderを定義する必要があり冗長性があるため、マークアップ拡張のほうが好みです。

サンプルプロジェクトはGitHubにアップしています。

列挙体

列挙体を定義する際に、Description属性を使用して列挙体の各値に文字列を紐付けておきます。今回はDisplay属性を使用していますが、Description属性等でも問題ありません(コンバーター要調整)

using System.ComponentModel.DataAnnotations;

namespace EnumComboBox
{
    enum Dogs
    {
        [Display(Name = "Golden Retriver")]
        GoldenRetriver,

        [Display(Name = "Miniture Dachshund")]
        MinitureDachshund,

        [Display(Name = "Border Collie")]
        BorderCollie,
    }
}

コンバーター

列挙体からDisplay属性に設定した文字列に変換するためのコンバートを用意します。

using System;
using System.Globalization;
using System.Windows.Data;
using System.Reflection;
using System.ComponentModel.DataAnnotations;

namespace EnumComboBox
{
    class EnumDisplayConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            FieldInfo field = value.GetType().GetField(value.ToString());
            DisplayAttribute attr = field.GetCustomAttribute<DisplayAttribute>();
            if (attr != null) {
                return attr.Name;
            }
            return value.ToString();
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

マークアップ拡張

列挙体の型からリストを作成するマークアップ拡張です。

using System;
using System.Windows.Markup;

namespace EnumComboBox
{
    public class EnumListExtension : MarkupExtension
    {
        private Type enumType_;

        public EnumListExtension(Type type)
        {
            enumType_ = type;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return Enum.GetValues(enumType_);
        }
    }
}

Xaml

上記の3つを利用したXamlは以下のように書きます。

<ComboBox ItemsSource="{Binding Source={local:EnumList {x:Type local:Dogs}}}">
    <ComboBox.Resources>
        <local:EnumDisplayConverter x:Key="EnumDisplayConv"/>
    </ComboBox.Resources>

    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Converter={StaticResource EnumDisplayConv}}" />
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>