1

WPFアプリケーションでMVVMパターンを使用し、ローカリゼーションを使用してDisplayAttributeをモデルのプロパティに適用します。

class MyModel
{
    [Display(
        ResourceType = typeof(Resources.Strings),
        Name = "MyPropertyName",
        Description = "MyPropertyDescription")]
    public double MyProperty { get; set; }
}

モデルプロパティのローカライズされた属性プロパティを表示したいと思います。必要なリソースファイルが適切な場所に配置され、必要なカルチャが設定されます。また、DisplayAttributeの必要に応じて、ビルドアクションを埋め込みリソースとして設定し、カスタムツールをPublicResXFileCodeGeneratorとして設定します。ただし、DisplayAttributeは、Nameパラメーターにローカライズされた文字列ではなく「MyPropertyName」を返します。

DisplayPropertyを使用するシナリオを以下に示します。モデルプロパティの属性プロパティを表示するには、次のようなヘルパークラスを持つコンバーターを使用します。

namespace MyProject.Converters
{
    // helper class
    public class MetadataParameters
    {
        public Type ModelType { get; set; }
        public string ModelProperty { get; set; }
        public Type AttributeType { get; set; }
        public string AttributeProperty { get; set; }
    }

    public class MetadataConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var mp = parameter as MetadataParameters;
            var modelPropertyInfo = mp.ModelType.GetProperty(mp.ModelProperty);
            var attribute = modelPropertyInfo
                .GetCustomAttributes(true)
                .Cast<Attribute>()
                .FirstOrDefault(memberInfo => memberInfo.GetType() == mp.AttributeType);
            var attributeProperty = attribute.GetType().GetProperty(mp.AttributeProperty);

            return attributeProperty.GetValue(attribute, null);
        }
    }
}

XAMLリソース:

xmlns:converters="clr-namespace:MyProject.Converters"
<converters:MetadataConverter x:Key="metadataConverter" />

XAML:

xmlns:converters="clr-namespace:MyProject.Converters"
xmlns:DataAnnotations="clr-namespace:System.ComponentModel.DataAnnotations;assembly=System.ComponentModel.DataAnnotations"
xmlns:Models="clr-namespace:MyProject.Models"

<TextBlock 
    <TextBlock.Text>
        <Binding
            Mode="OneWay"
            Converter="{StaticResource metadataConverter}">
            <Binding.ConverterParameter>
                <converters:MetadataParameters
                    ModelType="{x:Type Models:Model}"
                    ModelProperty="ModelProperty"
                    AttributeType="{x:Type DataAnnotations:DisplayAttribute}"
                    AttributeProperty="Name" />                            
            </Binding.ConverterParameter>
        </Binding>
    </TextBlock.Text>
</TextBlock>

独自の単純な属性を作成すると、プロパティの名前の値だけでなく、ローカライズされた文字列が返されます(DisplayAttributeのように)。

public class LocalizedDisplayAttribute : Attribute
{
    private readonly string name;

    public virtual string Name
    {
        get
        {
            var rm = new System.Resources.ResourceManager(typeof(Resources.Strings));
            return rm.GetString(this.name);
        }
    }

    public LocalizedDisplayAttribute(string name)
        : base()
    {
        this.name = name;
    }
}


class MyModel
{
    [LocalizedDisplay("MyPropertyName")]
    public double MyProperty { get; set; }
}

では、なぜDisplayAttributeがそのプロパティをローカライズしないのでしょうか。MSDNによると、この属性はローカリゼーション用に設計されたものです。そして、私のASP.NETMVCプロジェクトではDisplayAttributeが正しく機能します。

4

1 に答える 1

5

DisplayAttributeにはGetメソッドがあり、呼び出す必要のある他のいくつかのメソッドがあります。これにより、探しているローカライズされた文字列が得られます。ここで行っているのは、「MyPropertyName」であるDisplayAttribute.Nameプロパティの値を取得することです。

DisplayAttribute.GetName()を呼び出すと、ResourceTypeプロパティで指定されたリソースクラスが検索され、Nameプロパティを使用してプロパティがリソースタイプに反映されます。

リクエストしたAttributePropertyのGetXXX()メソッドを取得することを前提として、コードを変更しました。

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
  var mp = parameter as MetadataParameters;
  var modelPropertyInfo = mp.ModelType.GetProperty(mp.ModelProperty);
  var attribute = modelPropertyInfo
      .GetCustomAttributes(true)
      .Cast<Attribute>()
      .FirstOrDefault(memberInfo => memberInfo.GetType() == mp.AttributeType);

  // We have to call the GetXXX methods on the attribute to get a localised result.
  //return ((DisplayAttribute)attribute).GetName();
  var result = attribute
    .GetType()
    .InvokeMember(
      "Get" + mp.AttributeProperty,
      BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public,
      null,
      attribute,
      new object[0]); 
  return result;
}
于 2013-05-09T12:17:47.593 に答える