さびぬきのネタ

Rubyをよく使います。最近はC#もやや使います。C言語も少し使います。Reactは勉強中です。

WPFでコンボボックスの値をenumでBindingするMarkup拡張を実装する

WPFでUIの表示切替のモードを選択するコンボボックスを作成するときに、コンボボックスの値をenumで定義できれば、内部処理の条件判定などがいい感じになるのでは?と思ったので色々と調べてみたところ、PrismのコミッターであるBrian Lagunasさんの解説動画がYoutubeにありました。 マークアップ拡張について、何となくの理解でいると応用が利かなそうなので、細かいところを勉強用にメモを残します。

Brian Lagunasさんの解説動画

詳しい解説やソースコードはBrian Lagunasさんの動画を参照してください。

  1. enumとコンボボックスのバインドについて解説されています。
    https://www.youtube.com/watch?v=Bp5LFXjwtQ0

  2. enumローカライズについて解説されています。
    https://www.youtube.com/watch?v=T1mhORJCDsY

enumとコンボボックスをBindingする

解説動画1では、MarkupExtensionを継承したEnumBindingSourceExtensionクラスを作成しています。 MarkupExtensionを継承したクラスはサフィックスにExtensionを付けるとExtensionを除いた名前で参照することができます。 そのため、解説動画ではxamlからはEnumBindingSourceで参照されています。

<ComboBox ItemSource="{Binding Source={local:EnumBindingSource {x:Type local:Status}}}"/>

EnumBindingSourceExtensionクラスのコンストラクタに引数としてx:Type local:StatusでStatusの型を渡しています。

MarkupExtensionクラスにはターゲットプロパティ値を返却するProvideValueメソッドが定義されています。 ProvideValueメソッドは抽象メソッドなので、派生クラスでは処理の実装を定義しないといけません。今回はenumの値すべてをコンボボックスの要素としてバインドしたいので、Enum.GetValues(EnumType)を返却しています。

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

解説動画1の内容では、enumの値がそのままコンボボックスの選択肢に表示されます。

↓のenumの型をEnumBindingSourceExtensionのコンストラクタに引数で渡すとコンボボックスの選択肢はHoge Fuga HogeFugaになります。

public enum Hoge
{
    Hoge,
    Fuga,
    HogeFuga,
}

Description属性からコンボボックスに表示する文字を設定する

解説動画1では、enumの値がそのままコンボボックスに選択肢で表示されます。 このままでも利用はできますが、正直使い勝手は悪いです。おそらく日本語の選択肢で表示できる方が何かと都合がよさそうですよね。

解説動画2では、enumに定義したDescription属性の文字列をコンボボックスに表示する処理を実装しています。

↓の例だとコンボボックスの選択肢がほげ ふが ほげふがになります。

[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum Hoge
{
    [Description("ほげ")]
    Hoge,
    [Description("ふが")]
    Fuga,
    [Description("ほげふが")]
    HogeFuga,
}

enumの値をDescriptionに定義されている文字列で返却したいので、enumからstringに値の返却時に型を変換しないといけません。

今回はstring型に変換するEnumDescriptionTypeConverterクラスを作成しています。 EnumDescriptionTypeConverterクラスはTypeConverter属性からenumの値を変換するため、EnumConverterクラスを継承しています。 ConvertToメソッドは対象のvalueオブジェクトを任意の型に変換するメソッドです。 enum値のFieldInfoを参照して、DescriptionAttribute(Description属性)があればその値をstringに変換して返却して、DescriptionAttributeが取得できなかった場合はenum値をstringに変換して返却しています。

まとめ

viewとenumなどを紐づける場合は下記の実装プロセスになる。

MarkupExtensionの機能によるviewとenum値をバインドするenumにDescription属性で値に文字列のメタ情報を付与するTypeConverterを使用してenumの返却値をstring型に変換してDescription属性の文字列を返却する

WPFではこういった拡張を実装することが多いですよね。