スポンサーリンク

2016年5月18日水曜日

HtmlHelper で Enum 型の値に設定した DisplayAttribute の名称を取得する

 ASP.NET MVC での話。
 DisplayAttribute によってフィールドに表示名が指定された列挙型の値を、View(Razor) で表示しようとしても表示されない。
 HtmlHelper.DisplayFor メソッドを使用しても列挙値のフィールド名が表示されるだけで DisplayAttribute で指定した表示名を引っ張ってくれない。

 いろいろ調べて、HtmlHelper の拡張メソッドを自作することで解決したのでメモ。
 以下、前振りから。


 モデルにあるプロパティが定義されてあって、そのプロパティに DisplayNameAttribute によって表示名称が取得されている場合、それを表示するのに HtmlHelper.DisplayNameFor メソッドを使用する。DisplayNameAttribute が設定されていなければプロパティ名がそのまま表示される。
 たとえば以下のようにプロパティが定義されている場合。

[DisplayName("変数名1")]
public string ParameterName1 { get; set; }

[DisplayName("値1")]
public string Value1 { get; set; }

public string ParameterName2 { get; set; }

public string Value2 { get; set; }

 View(Razor) で以下のように書くと、

<dt>
    @Html.DisplayNameFor(model => model.ParameterName1)
</dt>
<dd>
    @Html.DisplayFor(model => model.ParameterName1)
</dd>
<dt>
    @Html.DisplayNameFor(model => model.Value1)
</dt>
<dd>
    @Html.DisplayFor(model => model.Value1)
</dd>
<dt>
    @Html.DisplayNameFor(model => model.ParameterName2)
</dt>
<dd>
    @Html.DisplayFor(model => model.ParameterName2)
</dd>
<dt>
    @Html.DisplayNameFor(model => model.Value2)
</dt>
<dd>
    @Html.DisplayFor(model => model.Value2)
</dd>

 表示はこんな感じに。


 値を表示するときは HtmlHelper.DisplayFor メソッドを使用。

 さて、このプロパティが以下のような列挙型で、

public enum ParameterType : int
{
    [Display(Name = "固定値")]
    FixedValue = 0,
    [Display(Name = "漢字氏名")]
    UserName,
    [Display(Name = "メールアドレス")]
    MailAddress,
    [Display(Name = "年度")]
    Year,
}

 プロパティの定義が以下だった場合。

[DisplayName("変数名1")]
public string ParameterName1 { get; set; }

[DisplayName("値1")]
public ParameterType Value1 { get; set; }

public string ParameterName2 { get; set; }

public ParameterType Value2 { get; set; }

 表示は以下のようになってしまう。


 普通、列挙値に DisplayAttribute を指定して表示名を指定しているわけなので、最初と同じように、


 と表示して欲しい。

 というわけで、HtmlHelper に拡張メソッド DisplayEnumFor を自作して解決。

public static IHtmlString DisplayEnumFor<TModel, TProp>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProp>> expression)
{
    var value = ModelMetadata.FromLambdaExpression(expression, html.ViewData).Model;

    if (!Enum.IsDefined(typeof(TProp), value))
    {
        throw new ArgumentException();
    }

    var field = value.GetType().GetField(value.ToString());
    var attributes = field.GetCustomAttributes(typeof(DisplayAttribute), false) as DisplayAttribute[];

    if (attributes == null || attributes.Length == 0)
    {
        return new HtmlString(value.ToString());
    }
    else
    {
        return new HtmlString(attributes[0].Name);
    }
}

 値を取り出して、列挙型であることを確認して、FieldInfo オブジェクトを取得。
 そこから DisplayAttribute を取得して Name プロパティを返す。
 引数が列挙型のプロパティじゃなかった場合は ArgumentException を発生させ、DisplayAttribute が指定されていなかった場合は素直に列挙値を返す。

 あとはこの拡張メソッドが定義された名前空間を Views フォルダ内の web.config に追記する。

<system.web.webPages.razor>
  <pages pageBaseType="System.Web.Mvc.WebViewPage">
    <namespaces>
      <add namespace="System.Web.Mvc" />
      <add namespace="System.Web.Mvc.Ajax" />
      <add namespace="System.Web.Mvc.Html" />
      <add namespace="System.Web.Optimization"/>
      <add namespace="System.Web.Routing" />
      <add namespace="Sample" />
      <add namespace="Sample.Extensions"/>
    </namespaces>
  </pages>
</system.web.webPages.razor>

 これで以下のように DisplayEnumFor メソッドを使用して列挙型のフィールドに DisplayAttribute で指定した表示名称が使える。

<dt>
    @Html.DisplayNameFor(model => model.ParameterName1)
</dt>
<dd>
    @Html.DisplayFor(model => model.ParameterName1)
</dd>
<dt>
    @Html.DisplayNameFor(model => model.Value1)
</dt>
<dd>
    @Html.DisplayEnumFor(model => model.Value1)
</dd>
<dt>
    @Html.DisplayNameFor(model => model.ParameterName2)
</dt>
<dd>
    @Html.DisplayFor(model => model.ParameterName2)
</dd>
<dt>
    @Html.DisplayNameFor(model => model.Value2)
</dt>
<dd>
    @Html.DisplayEnumFor(model => model.Value2)
</dd>

0 件のコメント:

コメントを投稿