mogmo .NET

C#/XAML/VB6たまにC++についてメモ程度に書いていく。あくまで自分用。責任は取れません。

Enumの値をコンボボックスに表示する(リソース文字列を使う)

アプリ開発をしていると,文字列はリソースで管理することが多い。
しかし,Display や Descriptionなどの属性引数にリソース文字列を使おうとすると,コンパイラ エラー CS0182(属性引数には定数式,typeof 式,または属性パラメーター型の配列の作成式でなければなりません。)となる。

これを回避する方法を考えた。

リソースファイル

こんな感じ。この記事ではTextJapanのみ扱う。
f:id:mogmo811:20181101100116p:plain

問題のコード

以下のように書くと,コンパイルエラーが発生する。

public enum DisplayResourceCountry
{
    [Display(Name = Properties.Resources.TextJapan)]
    Japane,
}

public enum DescriptionResourceCountry
{
    [Description(Properties.Resources.TextJapan)]
    Japane,
}

①DisplayAttributeの場合

DisplayAttributeのResourceTypeプロパティを使う

ResourceTypeプロパティにリソースを設定すると,Nameに指定した値をリソースキーとして認識してリソースから値をとってきてくれる仕組みになっているらしい?。
Nameプロパティには"Text.Japan"とリソースキーを直接打っても良いが,間違ったリソースキーも設定できてしまう。その対策として,nameof()で名前を文字列リテラルとして取得している。nameof演算子C#6からの機能です。

private const string nameTextJapan = nameof(Properties.Resources.TextJapan);
public enum DisplayResourceCountry
{
    [Display(Name = nameTextJapan, ResourceType = typeof(Properties.Resources))]
    Japane,
}

...が,コンボボックスではできなかった。おいおい調査するかも。
コンバータはやはり必要になる。

コンバーターを修正する。

ResourceTypeプロパティ設定されたリソースから,Nameに指定されたリソースキーを取得して返すようGetEnumNameメソッドのを修正した。

public class ComboBoxDisplayConverter : IValueConverter
{
    private string GetEnumName(Enum enumObject)
    {
        if (enumObject == null) return "";
        FieldInfo fieldInfo = enumObject.GetType().GetField(enumObject.ToString());

        object[] attributeArray = fieldInfo.GetCustomAttributes(false);

        if (attributeArray.Length == 0)
        {
            return enumObject.ToString();
        }
        else
        {
           DisplayAttribute attribute = attributeArray[0] as DisplayAttribute;
            if (attribute == null) return string.Empty;
            if (attribute.ResourceType == null) return attribute.Name;
            if (attribute.ResourceType.Name != "Resources") return attribute.Name;
            var resourceManager = new ResourceManager(attribute.ResourceType);
            if (resourceManager == null) return attribute.Name;
            return resourceManager.GetString(attribute.Name);
        }
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Enum myEnum = (Enum)value;
        string description = GetEnumName(myEnum);
        return description;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return string.Empty;
    }
}

②DescriptionAttributeの場合

DescriptionAttributeにはResourceTypeプロパティが無いので,扱うリソースファイルが一つの時のみコンバーターで対応可能。

DescriptionAttribute attribute = attributeArray[0] as DescriptionAttribute;
if (attribute == null) return string.Empty;
var resourceManager = new ResourceManager(typeof(Properties.Resources));
return resourceManager.GetString(attribute.Description);

完成

できた~
f:id:mogmo811:20181101100157p:plain