Kobarin's Development Blog

C#やASP.NETなどについての記録です。

ASP.NET MVC のDisplayFormatを表示用と編集用で分ける方法

タイトルが分かりにくいですが、正確には

  • DisplayFormat属性のDataFormatStringを、表示のみする場面(例:Index、Details)と編集する場面(例:Create、Edit)で別々に作る

といった内容です。

結論「別々に作る事はできない」

という事で、当然ですがDataFormatStringは1つしか作れないため、プロパティ自体を表示用と編集用に別々に用意する事で対応しましたが、とりあえず1つ1つ流れを見ていきます。

例えば、以下のようなProductエンティティがあるとします。

public class Product {
  public int ProductID { get; set; }
  …
  public decimal StandardCost { get; set; }
  …
}

まずはDisplayFormat

StandardCostはdecimal型なので、上記のままだと表示時も編集時も「1000.00」のような表記になりますが、このままだと見にくいと感じます。
そこで、期待する表記は「3桁区切りカンマ、小数点以下は必要次第で表示」だとします。

  • 例1:「1000」の場合なら「1,000」
  • 例2:「1234.5」の場合なら「1,234.5」

そこでDisplayFormatを下記のように記述します。

public class Product {
  …
  [DisplayFormat(DataFormatString = "{0:#,##0.#}", ApplyFormatInEditMode = true)]
  public decimal StandardCost { get; set; }
  …
}

ApplyFormatInEditMode は、編集時でも同じフォーマットを適用する場合に使う属性です(既定でfalse)。

編集の時はカンマ不要

これで期待する通りの表示になりますが、困った事に編集用のTextboxでも「1,000」のようにカンマが現れてしまいます。
しかもそのままだと検証に引っかかり「The field StandardCost must be a number(StandardCostは数字でなければなりません)」と修正を求められます。変更もないのにカンマを削除するのは面倒ですよね。


それでは…とApplyFormatInEditModeを取り除いてみます。

public class Product {
  …
  [DisplayFormat(DataFormatString = "{0:#,##0.#}")]
  public decimal StandardCost { get; set; }
  …
}

こうすると表示は変化ありませんが、編集では「1000.00」と小数点以下の余計な部分が現れてしまいます。
このままでも問題はないのですが、何となく鬱陶しいですよね。

DisplayFormatの限界…(面倒だけど)プロパティを2つ作る

このようにDisplayFormatは、表示と編集とで表記を統一する機能はあっても、別々に定義することが出来ないのです。
そこで苦肉の策となりますが、別々に定義するのであれば、以下のようにプロパティを2つに分けるしかありません。

public class Product {

  // 編集用
  [DisplayFormat(DataFormatString = "{0:#0.#}", ApplyFormatInEditMode = true)]
  public decimal StandardCost { get; set; }

  // 表示用
  [DisplayFormat(DataFormatString = "{0:#,##0.#}")]
  public decimal StandardCostDisplay { get { return StandardCost; } }
}

「表示用」でStandardCostDisplayというStandardCostを参照するだけのプロパティを作り、希望通りのフォーマットにします。
一方、「編集用」ではApplyFormatInEditModeを付け、カンマは適用しないフォーマットにします。

Viewに少し修正を加えて終了

最後に、View側を少し修正し、IndexやDetailsといった表示のViewではStandardCostDisplayをバインドし、CreateやEditはStandardCostをバインドさせます。

Index, Details
  <td>
    @Html.DisplayFor(model => model.StandardCostDisplay)
  </td>
Create, Edit
  @Html.EditorFor(model => model.StandardCost, new { htmlAttributes = new { @class = "form-control" } })

希望

本当はEditFormatみたいな属性があれば最も話が早いと思うんですが…まぁ現状ではこれしか思いつきませんでした。