Help us understand the problem. What is going on with this article?

Enum をローカライズして WPF でコンボボックスなどにバインドする方法

Infragistics のコンポーネント作ってる中の人かつ WPF/Xamarin.Forms でアプリ開発している人なら名前を聞いたことがない人はいないくらい有名な Prism のコミッターで筋肉が凄い Brian Lagunas さんの YouTube チャンネルで先日こんなネタが取り上げられていました。

How to Bind an Enum to a ComboBox in WPF

この動画で Enum の要素を簡単に ComboBox の選択肢として出す方法が紹介されていたのですが、ローカライズは考慮していなかったので、いいブログネタだなと思ってました。
そう思ってたら、今日こんな動画があがってました。

How to Localize Enums in C#

ネタかぶったぁぁぁぁぁぁぁぁぁ!!

動画内の方法の紹介

マークアップ拡張で Enum 型の値の配列を返すものを作ることで以下のように簡単に書ける。

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

Enum を任意の文字列に変換するには、TypeConverter を使って Enum が WPF のコントロールに表示される処理をカスタマイズして DescriptionAttribute があったら、そこに指定されている値を使うという感じでした。ローカライズは LocalizedDescriptionAttirubute を使ってリソースの定義されている型とキーを指定できる独自の属性を定義する感じにしてました。

なるほど、TypeConverter の存在を忘れかけてましたが、こういう風にも使えるんですね。

ちょっとだけ改善?

動画内では DescriptionAttribute を使って Enum に表示用文字列を指定していて、ローカライズ可能にする場合は自前のローカライズした文字列を指定可能な属性を自作していましたが、一応標準の中にもローカライズ機能つきの属性として DisplayAttribute があるので、そっちを使えば独自属性が無くてもよさそうかなって思いました。

ただ、純粋にローカライズ可能な表示名だけを提供したい場合にはオーバースペックなのでカスタム属性のほうがいいかもしれません。でもとりあえずやってみましょう。

TypeConverter を以下のようにして DisplayAttribute を使うようにして…

EnumDisplayTypeConverter.cs
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Reflection;

namespace BindingEnums
{
    public class EnumDisplayTypeConverter : EnumConverter
    {
        public EnumDisplayTypeConverter(Type type) : base(type)
        {
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                if (value != null)
                {
                    var field = value.GetType().GetField(value.ToString());
                    if (field != null)
                    {
                        var attribute = field.GetCustomAttribute<DisplayAttribute>(false);
                        return attribute == null ? value.ToString() : attribute.GetName();
                    }
                }
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }

    }
}

そして、Enum に以下のように Display 属性つきで定義します。

Status.cs
using BindingEnums.Resources;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Text;

namespace BindingEnums
{
    [TypeConverter(typeof(EnumDisplayTypeConverter))]
    public enum Status
    {
        [Display(ResourceType = typeof(EnumResource), Name = nameof(EnumResource.Horrible))]
        Horrible,
        [Display(ResourceType = typeof(EnumResource), Name = nameof(EnumResource.Bad))]
        Bad,
        [Display(ResourceType = typeof(EnumResource), Name = nameof(EnumResource.SoSo))]
        SoSo,
        [Display(ResourceType = typeof(EnumResource), Name = nameof(EnumResource.Good))]
        Good,
        [Display(ResourceType = typeof(EnumResource), Name = nameof(EnumResource.Better))]
        Better,
        [Display(ResourceType = typeof(EnumResource), Name = nameof(EnumResource.Best))]
        Best,
    }
}

リソースは間に合わせで適当に用意しておきました。

image.png

実行すると、こんな感じになります。

image.png

いいね。

まとめ

ということで自分でリソースから文字列取ってくる処理をしなくても、標準である属性の中だと DisplayAttribute あたりを使うと自前で属性作らなくてもいけるけど、若干 DisplayAttribute はオーバースペック感があるのは否めない…。

ソースコードは以下のリポジトリにアップしておきました。

https://github.com/runceel/BindingEnums

あと Brian Lagunas さんの YouTube チャンネルは XAML 系のことをやってる人にとっては身近な話題で英語が聞けるし、自動生成字幕でもかなりイケてるので個人的におすすめです。

Brian Lagunas さんの YouTube チャンネルは以下のリンクからいけます。

Brian Lagunas - YouTube

okazuki
日本マイクロソフトでサポート系のエンジニアとして働いています。 好きな言語は C# と TypeScript。メインの興味領域は Windows クライアントアプリ開発と Xamarin によるモバイルアプリ開発。その延長として API を作るための Azure の PaaS 系サービスが好きです。 SPA はたしなむ程度に。 お約束ですが、ここでの発言は個人の見解になります。
https://blog.okazuki.jp
microsoft
マイクロソフトのメンバーが最新の技術情報をお届けします。Twitterアカウント(@msdevjp)やYouTubeチャンネル「クラウドデベロッパーちゃんねる」も運用中です。
https://aka.ms/MSFT-Docs-JPN
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした