2016年12月28日水曜日

ASP.NET MVC 13_モデルバインド

VisualStuidioCommunity2015/Fw4.5.2/C#


前回まではモデルの値をビューに表示する方法を見てきました。
今回はビューの値をコントローラーで受け取る方法を見ていきたいと思います。

モデルバインド

モデルバインドとはクライアントからの入力値を、アクションメソッドの引数に自動的に割り当てる機能のことです。
ポストデータやクエリーパラメータ、ルートパラメータなど、リクエストデータと同名の引数(大文字小文字は区別されない)をアクションメソッドに用意しておくだけで、自動的に割り当ててくれます。

まずはモデルバインドの基本的な動作を確認してみます。
以下の例では、ビューに用意したテキストボックス「StringValue」で入力した値が、アクションメソッド「SimpleBind」に用意された同名の引数「stringvalue」にバインドされます。

ビュー
@using (Html.BeginForm("SimpleBind", "ModelBind"))
{
    <dl>
        <dt>@Html.Label("String値:")</dt>
        <dd>@Html.TextBox("StringValue")</dd>
    </dl>
    <div>
        <input type="submit" value="送信" />
    </div>
}
コントローラー
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class ModelBindController : Controller
    {

        public ActionResult Show()
        {
            return View();
        }

        public ActionResult SimpleBind(string stringvalue)
        {
            string value = $"「{stringvalue}」が入力されました。";
            return Content(value);
        }
    }
}



上記の例はアクションメソッドの引数がstring型でしたが、int型に変えてみます。

ビュー
@using (Html.BeginForm("SimpleBind", "ModelBind"))
{
    <dl>
        <dt>@Html.Label("int値:")</dt>
        <dd>@Html.TextBox("IntValue")</dd>
    </dl>
    <div>
        <input type="submit" value="送信" />
    </div>
}
コントローラー
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class ModelBindController : Controller
    {

        public ActionResult Show()
        {
            return View();
        }

        public ActionResult SimpleBind(int intvalue)
        {
            string value = $"「{intvalue}」が入力されました。";
            return Content(value);
        }
    }
}
テキストボックスに何も入力せずに送信したり、数値に変換できない文字を入力して送信すると、「アクションパラメータの引数にnullが設定できないよ」とエラーになってしまいます。

モデルバインダーは、リクエストデータに値が設定されていないときや、アクションメソッドの同名の引数に指定した型に変換できない場合は、引数にnullを設定しようとします。

アクションメソッドの引数が値型の場合、nullを受け入れられるようにnullable型にしておく必要があります。
public ActionResult SimpleBind(int? intvalue)
{
    string value = $"「{intvalue}」が入力されました。";
    return Content(value);
}


アクションメソッドの引数にモデルを指定すると、リクエストデータと同名のプロパティに値をセットしてくれます。
モデルのプロパティが値型の場合、必須でなければnullableにしておきます。

モデル
using System.ComponentModel.DataAnnotations;

namespace Practice.Models
{
    public class BindSampleViewModel
    {
        [Display(Name = "string値")]
        public string StringValue { get; set; }

        [Display(Name = "int値")]
        public int? IntValue { get; set; }
    }
}
ビュー
@model Practice01_Begin.Models.BindSampleViewModel

@using (Html.BeginForm("BindSampleResult", "ModelBind"))
{
<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.StringValue)</dt>
    <dd>@Html.EditorFor(mdl => mdl.StringValue)</dd>
    <dt>@Html.DisplayNameFor(mdl => mdl.IntValue)</dt>
    <dd>@Html.EditorFor(mdl => mdl.IntValue)</dd>
</dl>
<input type = "submit" value = "送信" />
コントローラー
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class ModelBindController : Controller
    {
        public ActionResult BindSample()
        {
            return View();
        }
        public ActionResult BindSampleResult(Models.BindSampleViewModel mdl)
        {
            string value = $"「{mdl.StringValue},{mdl.IntValue}」が入力されました。";
            return Content(value);
        }
    }
}


オーバーポスティング攻撃の対策

アクションメソッドの引数にモデルを指定する場合は「オーバーポスティング攻撃」を受ける可能性が出てきます。
たとえば以下のように、モデルにビューに表示するだけのプロパティ「ImportantValue」があったとします。
using System.ComponentModel.DataAnnotations;

namespace Practice01_Begin.Models
{
    public class BindSampleViewModel
    {
        [Display(Name = "string値")]
        public string StringValue { get; set; }

        [Display(Name = "int値")]
        public int? IntValue { get; set; }

        [Display(Name = "権限")]
        public string Role { get; set; }
    }
}
ビューはStringValueとIntValueのみが編集でき、Roleは表示するだけになっていたとします。
@model Practice01_Begin.Models.BindSampleViewModel

@using (Html.BeginForm("BindSampleResult", "ModelBind"))
{
<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.StringValue)</dt>
    <dd>@Html.EditorFor(mdl => mdl.StringValue)</dd>
    <dt>@Html.DisplayNameFor(mdl => mdl.IntValue)</dt>
    <dd>@Html.EditorFor(mdl => mdl.IntValue)</dd>
    <dd>@Html.DisplayNameFor(mdl => mdl.Role)</dd>
    <dd>@Html.DisplayFor(mdl => mdl.Role)</dd>
</dl>
<input type = "submit" value = "送信" />
コントローラーは先ほどと同じで、引数にモデルを指定します。
ただし、モデルのRoleの値が’admin’であれば、何かしら重要な処理をするとします。
このとき悪意あるユーザーがポストデータのRoleを「admin」に改ざんしたデータを送信すると、意図しない動きになってしまいます。
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class ModelBindController : Controller
    {
        public ActionResult BindSample()
        {
            var mdl = new Models.BindSampleViewModel();
            mdl.StringValue = "StringValue";
            mdl.IntValue = 12345;
            mdl.Role = "user";
            return View(mdl);
        }  
       public ActionResult BindSampleResult(Models.BindSampleViewModel mdl)
 {
     if (mdl.Role != null)
     {
         //"重要な処理をする";
     }
     string str = $"「StringValue:={mdl.StringValue},IntValue:={mdl.IntValue}, Role:={mdl.Role}」が入力されました。";
     return Content(str);
     }
 }
}
自動的にバインドしたくないプロパティがある場合は、
「バインドするプロパティを明示的に指定する」か「バインドしないプロパティを指定する」ことでオーバーポスティング攻撃を防御できます。

バインドするプロパティを明示的に指定するには、
コントローラーのアクションメソッドの引数にBind属性をつけ、Includeプロパティにバインドするプロパティ名を設定します。
public ActionResult BindSampleResult
([Bind(Include = "StringValue, IntValue")] Models.BindSampleViewModel mdl)
{
    ・・・略
}
バインドしないプロパティを指定するには、
コントローラーのアクションメソッドの引数にBind属性をつけ、Excludeプロパティにバインドしないプロパティ名を設定します。
public ActionResult BindSampleResult
([Bind(Exclude = "Role")] Models.BindSampleViewModel mdl)
{
    ・・・略
}
ためしにビューでRoleプロパティを入力できるようにして実行してみます。

Bind属性をつけるとRoleには入力値がバインドされていません。

Bind属性を外すとRoleniha入力値がバインドされています。


IncrudeプロパティかExcludeプロパティのどちらを使用すればよいか、参考にしている本「実践プログラミング ASP.NET MVC5」では、原則としてIncludeプロパティを利用すべきとの記載があります。
Excludeプロパティではあとからプロパティを追加した場合、Exclude定義の更新を忘れて意図しないバインドを受け入れてしまう可能性があり
Includeプロパティではあとからプロパティを追加した場合、Include定義の更新を忘れても、値がバインドされていないという明確な結果になり、問題もすぐ特定できるためとありました。

2016年12月24日土曜日

ASP.NET MVC 12_テンプレートヘルパー ~その2
DataType属性、DisplayFormat属性

VisualStuidioCommunity2015/Fw4.5.2/C#


DisplayForヘルパーはモデルの値を表示形式で出力します。
EditorForヘルパーはモデルの値を入力形式で出力します。
モデルの値の型やDataType属性に指定した値により出力形式が変わります。

DataType属性による DisplayForヘルパー/ EditorForヘルパー の出力の違い

前回はモデルの値の型(sring型、int型、long型、decimal型、DateTime型、bool型、bool?型、列挙型)による出力の違いをみました。
今回はDataType属性(名前空間:System.ComponentModel.DataAnnotations)による出力の違いをみたいと思います。

DataType.Text

DisplayForヘルパーでの出力は、モデルの値がそのまま出力されるだけで、DataType属性の影響はありません。
EditorForヘルパーでは、モデルの値がint型の場合は<input type="number" >と出力さるところ、DataType.Text属性を付けると<input type="text" >と出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [Display(Name = "int型(DataType.Text)")]
    [DataType(DataType.Text)]
    public int IntValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.IntValue = 123456789;
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.IntValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.IntValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.IntValue)</dd>
</dl>
出力
<dl>
    <dt>int型(DataType.Text)</dt>
    <dd>123456789</dd>
    <dd><input class="text-box single-line" data-val="true" 
     data-val-number="フィールド int型(DataType.Text) には数字を指定してください。" 
     data-val-required="int型(DataType.Text) フィールドが必要です。" 
     id="IntValue" name="IntValue" type="text" value="123456789" /></dd>
</dl>

DataType.Html

DisplayForヘルパーでの出力は通常HTMLエンコードされて出力されますが、DataType.Html属性をつけるとエンコードされずに出力されます。
EditorForヘルパーでの出力には、DataType属性の影響はありません。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [Display(Name = "string型(DataType.Html)")]
    [DataType(DataType.Html)]
    public string HtmlValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.HtmlValue = "<font color='red'>赤字</font>";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.HtmlValue)</dt>   
    <dd>@Html.DisplayFor(mdl => mdl.HtmlValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.HtmlValue)</dd>
</dl>
出力
<dl>
    <dt>string型(DataType.Html)</dt>   
    <dd><font color='red'>赤字</font></dd>
    <dd><input class="text-box single-line" id="HtmlValue" name="HtmlValue" type="text" 
        value="&lt;font color=&#39;red&#39;&t;赤字&lt;/font&gt;" /></dd>
</dl>

DataType.MultilineText

DisplayForヘルパーでの出力には影響がありません。
EditorForヘルパーでの出力では、テキストエリアが出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [Display(Name = "string型(DataType.MultilineText)")]
    [DataType(DataType.MultilineText)]
    public string MultilineTextValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.MultilineTextValue = "1行目\r\n2行目";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.MultilineTextValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.MultilineTextValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.MultilineTextValue)</dd>
</dl>

出力
<dl>
<dt>string型(DataType.MultilineText)</dt>
        <dd>1行目
            2行目</dd>
        <dd><textarea class="text-box multi-line" id="MultilineTextValue" 
        name="MultilineTextValue">
        1行目
        2行目</textarea></dd>
</dl>

DataType.EmailAddress

DisplayForヘルパーではメールリンク(<a href="mailto:~">)が出力されます。
EditorForヘルパーではテキストボックス(<input type="email" >)が出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
 [Display(Name = "string型(DataType.EmailAddress)")]
 [DataType(DataType.EmailAddress)]
 public string EmailAddressValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.EmailAddressValue = "aaa@gmail.com";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.MultilineTextValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.MultilineTextValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.MultilineTextValue)</dd>
</dl>

出力
<dl>
    <dt>string型(DataType.EmailAddress)</dt>
    <dd><a href="mailto:aaa@gmail.com">aaa@gmail.com</a></dd>
    <dd><input class="text-box single-line" id="EmailAddressValue" 
        name="EmailAddressValue" type="email" value="aaa@gmail.com" /></dd>
</dl>

DataType.Url

DisplayForヘルパーではハイパーリンク(<a href="~">)が出力されます。
EditorForヘルパーではテキストボックス(<input type="url" >)が出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [Display(Name = "string型(DataType.Url)")]
    [DataType(DataType.Url)]
    public string UrlValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.UrlValue = "https://www.google.co.jp";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.UrlValue)</dt>
        <dd>@Html.DisplayFor(mdl => mdl.UrlValue)</dd>
        <dd>@Html.EditorFor(mdl => mdl.UrlValue)</dd>
</dl>

出力
<dl>
<dt>string型(DataType.Url)</dt>
        <dd><a href="https://www.google.co.jp">https://www.google.co.jp</a></dd>
        <dd><input class="text-box single-line" id="UrlValue" name="UrlValue" type="url" value="https://www.google.co.jp" /></dd>
</dl>

DataType.Password

DisplayForヘルパーでの出力には影響がありません。
EditorForヘルパーではテキストボックス(<input type="password" >)が出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
        [Display(Name = "string型(DataType.Password)")]
        [DataType(DataType.Password )]
        public string PasswordValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.PasswordValue  = "ABCD123";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.PasswordValue)</dt>
        <dd>@Html.DisplayFor(mdl => mdl.PasswordValue)</dd>
        <dd>@Html.EditorFor(mdl => mdl.PasswordValue)</dd>
</dl>

出力
<dl>
 <dt>string型(DataType.Password)</dt>
        <dd>ABCD123</dd>
        <dd><input class="text-box single-line password" id="PasswordValue" name="PasswordValue" type="password" value="ABCD123" /></dd>
</dl>

DataType.PhoneNumber

DisplayForヘルパーでの出力には影響がありません。
EditorForヘルパーではテキストボックス(<input type="tel" >)が出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
        [Display(Name = "string型(DataType.PhoneNumber)")]
        [DataType(DataType.PhoneNumber )]
        public string PhoneNumberValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.PhoneNumberValue  = "09012345678";
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.PhoneNumberValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.PhoneNumberValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.PhoneNumberValue)</dd>
</dl>

出力
<dl>
    <dt>string型(DataType.PhoneNumber)</dt>
    <dd>09012345678</dd>
    <dd><input class="text-box single-line" id="PhoneNumberValue" name="PhoneNumberValue" type="tel" value="09012345678" /></dd>
</dl>

DataType.DateTime、DataType.Date、DataType.Time

DisplayForヘルパーではDataType.DateTimeでは年月日時分秒が出力され、DataType.Dateでは年月日が、DataType.Timeでは時分が出力されます。
EditorForヘルパーではそれぞれにテキストボックス(<input type="datetime" >、<input type="date" >、<input type="time" >)が出力されます。

DataType.Date、DateType.TimeではEditorForで値が出力されていません。
後述のDataFormat属性をつけると出力されます。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [DataType(DataType.DateTime)]
    public DateTime DateTimeValue { get; set; }

    [Display(Name = "DateTime(DataType.Date)")]
    [DataType(DataType.Date)]
    public DateTime DateValue { get; set; }

    [Display(Name = "DateTime(DataType.Time)")]
    [DataType(DataType.Time)]
    public DateTime TimeValue { get; set; }}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.DateTimeValue = DateTime.Now;
    mdl.DateValue = DateTime.Now;
    mdl.TimeValue = DateTime.Now;
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.DateTimeValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.DateTimeValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.DateTimeValue)</dd>

    <dt>@Html.DisplayNameFor(mdl => mdl.DateValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.DateValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.DateValue)</dd>

    <dt>@Html.DisplayNameFor(mdl => mdl.TimeValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.TimeValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.TimeValue)</dd>
</dl>

出力
<dl>
    <dt>DateTime(DataType.DateTme)</dt>
    <dd>2016/12/23 6:11:56</dd>
    <dd><input class="text-box single-line" data-val="true" 
        data-val-date="フィールド DateTime(DataType.DateTme) は日付である必要があります。" 
        data-val-required="DateTime(DataType.DateTme) フィールドが必要です。" 
        id="DateTimeValue" name="DateTimeValue" type="datetime" value="2016/12/23 6:11:56" /></dd>

    <dt>DateTime(DataType.Date)</dt>
    <dd>2016/12/23</dd>
    <dd><input class="text-box single-line" 
        data-val="true" data-val-date="フィールド DateTime(DataType.Date) は日付である必要があります。" 
        data-val-required="DateTime(DataType.Date) フィールドが必要です。" 
        id="DateValue" name="DateValue" type="date" value="2016/12/23" /></dd>

    <dt>DateTime(DataType.Time)</dt>
    <dd>6:11</dd>
    <dd><input class="text-box single-line" 
        data-val="true" data-val-required="DateTime(DataType.Time) フィールドが必要です。" 
        id="TimeValue" name="TimeValue" type="time" value="6:11" /></dd>
</dl>

先ほどDataType属性でDataType.DateやDateType.Timeを指定すると、EditorForで出力されたテキストボックスに値が出力されていませんでした。
これはDataFormat属性(名前空間:System.ComponentModel.DataAnnotations)で書式設定方法を指定すると出力されるようになります。


DisplayFormat属性のDataFormatStringプロパティに書式文字列を設定します。
ApplyFormatInEditModeプロパティには編集時にも書式を適用する場合にtrueを設定します。
DisplayFormat属性には他にもプロパティがあります。(後述のまとめ参照)

しかし、DateType.Dateの場合は編集時の書式が「yyyy-MM-dd」ではなく「yyyy/MM/dd」になっています。
ためしに書式を「{0:yyyy年MM月dd日}」としたところ、やはり値が出力されませんでした。
chrome(バージョン:55.0.2883.87)では<intput type="date" >の要素にフォーカスすると、カレンダーが表示されますが、おそらくこのカレンダーが「/」しか対応していないのではないでしょうか。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [DataType(DataType.DateTime)]
    [DisplayFormat(DataFormatString = "{0:yy年MM月dd日}", ApplyFormatInEditMode = true)]
    public DateTime DateTimeValue { get; set; }

    [Display(Name = "DateTime(DataType.Date)")]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateTime DateValue { get; set; }

    [Display(Name = "DateTime(DataType.Time)")]
    [DataType(DataType.Time)]
    [DisplayFormat(DataFormatString = "{0:HH:mm}", ApplyFormatInEditMode = true)]
    public DateTime TimeValue { get; set; }
}

DataType.Currency

DataType.Currencyの場合、カレントカルチャ情報に合わせた出力がされます。
DisplayForヘルパーでは、カレントカルチャの通貨情報でフォーマットされ出力されました。
EditorForヘルパーではカレントカルチャの数値情報でフォーマットされ、テキストボックス(<input type="text" >)で出力されました。

モデル
using System.ComponentModel.DataAnnotations;

public class AttributeModel
{
    [Display(Name = "Decimal(DataType.Currency)")]
    [DataType(DataType.Currency)]
    public Decimal CurrencyValue { get; set; }
}
コントローラー
public ActionResult AttributeAction()
{
    var mdl = new Models.AttributeModel();
    mdl.CurrencyValue = 12345678.5678m;
    return View(mdl);
}
ビュー
@model Practice.Models.AttributeModel

<dl>
    <dt>@Html.DisplayNameFor(mdl => mdl.CurrencyValue)</dt>
    <dd>@Html.DisplayFor(mdl => mdl.CurrencyValue)</dd>
    <dd>@Html.EditorFor(mdl => mdl.CurrencyValue)</dd>
</dl>

出力
<dl>
    <dt>Decimal(DataType.Currency)</dt>
    <dd>¥12,345,679</dd>
    <dd><input class="text-box single-line" data-val="true" 
        data-val-number="フィールド Decimal(DataType.Currency) には数字を指定してください。" 
        data-val-required="Decimal(DataType.Currency) フィールドが必要です。" 
        id="CurrencyValue" name="CurrencyValue" type="text" value="12345678.57" /></dd>
</dl>



まとめ

主なDataType属性
DataTypeDisplaryForヘルパーEditorForヘルパー
DataType.Textテキストボックス
<input type="text" >
DataType.Html値をエンコードせずに出力するテキストボックス
<input type="text" >
DataType.MultilineTextテキストエリア
<textarea>
DataType.EmailAddressメールリンク
<a href="mailto:~">
テキストボックス
<input type="email" >
DataType.Urlハイパーリンク
<a href="~">
テキストボックス
<input type="url" >
DataType.Passwordテキストボックス
<input type="password" >
DataType.PhoneNumber テキストボックス
<input type="tel" >
DataType.DateTime年月日時分秒が出力されるテキストボックス
<input type="datetime" >
DataType.Date年月日が出力されるテキストボックス
<input type="date" >
DataType.Time時分が出力されるテキストボックス
<input type="time" >
DataType.Currencyカレントカルチャの金額情報でフォーマットされ出力されるカレントカルチャの数値情報でフォーマットされ出力される
<input type="time" >

DisplayFormat属性
DateFormatString書式文字列
ApplyFormatInEditMode編集時にも書式を適用するかどうか
ConvertEmptyStringToNull空文字列をnullに変換するかどうか
NullDisplayText値がnullの時に表示するテキスト

2016年12月18日日曜日

ASP.NET MVC 12_テンプレートヘルパー ~その1~

VisualStuidioCommunity2015/Fw4.5.2/C#


前回までは個別のHTMLヘルパーを見てきました。
今回はテンプレートヘルパーを見て行きます。

テンプレートヘルパーは、引数に指定したモデルのプロパティの型やその属性から自動的にHTML要素を生成してくれます。
たとえばstring型であればinput要素を、bool型であればcheckbox要素を生成するといった具合です。

DisplayNameForヘルパー

DisplayNameForヘルパーは、モデルのプロパティの表示名をhtmlタグで修飾せずに出力します。
属性を指定しない場合、プロパティ名をそのまま出力します。
DisplayName属性を指定した場合、指定した値を出力します。
Display属性のnameプロパティを指定した場合も、指定した値を出力します。

モデル
//DisplayName属性を使用する場合にインポートする
using System.ComponentModel;
//Display属性を使用する場合にインポートする
using System.ComponentModel.DataAnnotations;

namespace Practice.Models
{
    public class TemplateHelperViewModel
    {
        public string Text1 { get; set; }

        [DisplayName("テキスト2")]
        public string Text2 { get; set; }

        [Display(Name = "テキスト3")]
        public string Text3 { get; set; }
    }
}
コントローラー
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class TemplateHelperController : Controller
    {
        public ActionResult Index()
        {
            var mdl = new Models.TemplateHelperViewModel();
            return View(mdl);
        }
    }
}
ビュー
@model Practice.Models.TemplateHelperViewModel

@Html.DisplayNameFor(mdl => mdl.Text1)
@Html.DisplayNameFor(mdl => mdl.Text2)
@Html.DisplayNameFor(mdl => mdl.Text3)

DisplayName属性(名前空間:System.ComponentModel)は、表示名しか指定できませんが、
Display属性(名前空間:System.ComponentModel.DataAnnotations)は、表示名以外にも表示方法をカスタマイズするためのプロパティがあります。
たとえば表示名をリソースファイルから出力するなど、Display属性でなければできない事がありますので、より汎用的なDisplay属性を使用する方がよさそうです。

DisplayForヘルパー/ EditorForヘルパー

DisplayForヘルパーはモデルの値を表示形式で出力します。
EditorForヘルパーはモデルの値を入力形式で出力します。
モデルの値の型やDataType属性(名前空間:System.ComponentModel.DataAnnotations)に指定した値により出力形式が変わります。

モデルの型による出力

まずはモデルの値の型によってどのような出力がされるか見ていきます。
string型、int型、long型、decimal型、DateTime型、bool型、bool?型(Nullable)、列挙型についてみてみます。

モデル
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Practice.Models
{    
    public enum WeweathereType
    {
        [Display(Name = "晴れ")]
        sunny,
        [Display(Name = "曇")]
        cloudy,
        [Display(Name = "雨")]
        rainy
    }
    
    public class DisplayForViewModel
    {
        [Display(Name ="String型")]
        public string StringValue { get; set; }

        [Display(Name = "int型")]
        public int IntValue { get; set; }

        [Display(Name = "long型")]
        public long LongValue { get; set; }

        [Display(Name = "decimal型")]
        public decimal DecimalValue { get; set; }

        [Display(Name = "DateTime型")]
        public DateTime DateTimeValue { get; set; }

        [Display(Name = "bool型")]
        public bool BoolValue { get; set; }

        [Display(Name = "bool(Nullable)型")]
        public bool? NullableBoolValue { get; set; }

        [Display(Name = "enum型")]
        public WeweathereType EnumValue { get; set; }  
    }
}
コントローラー
using System;
using System.Collections.Generic;
using System.Web.Mvc;

namespace Practice.Controllers
{
    public class TemplateHelperController : Controller
    {
        public ActionResult Index()
        {
            var mdl = new Models.DisplayForViewModel();
            mdl.StringValue = "赤字";
            mdl.IntValue = 123456789;
            mdl.LongValue = 123456789012345;
            mdl.DecimalValue = 12345.99999m;
            mdl.DateTimeValue = new DateTime(2017,01,01);
            mdl.BoolValue = true;
            mdl.NullableBoolValue = true;
            mdl.EnumValue = Models.WeweathereType.rainy;
            return View(mdl);
        }
    }
}
ビュー
@model Practice.Models.Index

@Html.DisplayNameFor(mdl => mdl.StringValue)
@Html.DisplayFor(mdl => mdl.StringValue)
@Html.EditorFor(mdl => mdl.StringValue)
@Html.DisplayNameFor(mdl => mdl.IntValue)
@Html.DisplayFor(mdl => mdl.IntValue)
@Html.EditorFor(mdl => mdl.IntValue)
@Html.DisplayNameFor(mdl => mdl.LongValue)
@Html.DisplayFor(mdl => mdl.LongValue)
@Html.EditorFor(mdl => mdl.LongValue)
@Html.DisplayNameFor(mdl => mdl.DecimalValue)
@Html.DisplayFor(mdl => mdl.DecimalValue)
@Html.EditorFor(mdl => mdl.DecimalValue)
@Html.DisplayNameFor(mdl => mdl.DateTimeValue)
@Html.DisplayFor(mdl => mdl.DateTimeValue)
@Html.EditorFor(mdl => mdl.DateTimeValue)
@Html.DisplayNameFor(mdl => mdl.BoolValue)
@Html.DisplayFor(mdl => mdl.BoolValue)
@Html.EditorFor(mdl => mdl.BoolValue)
@Html.DisplayNameFor(mdl => mdl.NullableBoolValue)
@Html.DisplayFor(mdl => mdl.NullableBoolValue)
@Html.EditorFor(mdl => mdl.NullableBoolValue)
@Html.DisplayNameFor(mdl => mdl.EnumValue)
@Html.DisplayFor(mdl => mdl.EnumValue)
@Html.EditorFor(mdl => mdl.EnumValue)
出力

データ型による出力の違い
データ型DisplayForヘルパーの出力EditorForヘルパーの出力
string型htmlエンコードされて出力されるテキストボックス
<input type="text" >
int型テキストボックス
<input type="number" >
long型テキストボックス
<input type="number" >
decimal型値が丸められて出力されるテキストボックス
<input type="text" >
値が丸められて出力される。
DateTime型テキストボックス
<input type="datetime" >
bool型無効なチェックボックス
<input type="checkbox" disabled="disabled" >
hidden要素は出力されない
チェックボックス型
<input type="checkbox" >
Hidden要素(<input type="hidden" >)が出力される。
bool?型(Nullable)無効なドロップダウン
<select disabled="disabled">
ドロップダウン
<select>
列挙型テキストボックス
<input type="text" >
decimal型は予想に反してtype="text"として出力されました。
bool型はチェックボックスとして、Nullableなbool型はドロップダウンとして出力されます。
enum型はテキストボックスとして表示されましたが、コチラではドロップダウンとして表示されると記載されています。


<dl>
<dt>String型</dt>
        <dd><font color='red'>赤字</font></dd>
        <dd><input class="text-box single-line" id="StringValue" name="StringValue" type="text" 
         value="<font color='red'>赤字</font>" /></dd>
<dt>int型</dd>
        <dd>123456789</dd>
        <dd><input class="text-box single-line" data-val="true" 
         data-val-number="フィールド int型 には数字を指定してください。" 
         data-val-required="int型 フィールドが必要です。" id="IntValue" name="IntValue" 
         type="number" value="123456789" /></dd>
<dt>long型</dd>
        <dd>123456789012345</dd>
        <dd><input class="text-box single-line" data-val="true" 
         data-val-number="フィールド long型 には数字を指定してください。" 
         data-val-required="long型 フィールドが必要です。" 
         id="LongValue" name="LongValue" type="number" value="123456789012345" /></dd>
<dt>decimal型</dd>
        <dd>12346.00</dd>
        <dd><input class="text-box single-line" data-val="true" 
         data-val-number="フィールド decimal型 には数字を指定してください。" 
         data-val-required="decimal型 フィールドが必要です。" 
         id="DecimalValue" name="DecimalValue" type="text" value="12346.00" /></dd>
<dt>DateTime型</dd>
        <dd>2017/01/01 0:00:00</dd>
        <dd><input class="text-box single-line" data-val="true" 
         data-val-date="フィールド DateTime型 は日付である必要があります。" 
         data-val-required="DateTime型 フィールドが必要です。" id="DateTimeValue" 
         name="DateTimeValue" type="datetime" value="2017/01/01 0:00:00" /></dd>
<dt>bool型</dd>
        <dd><input checked="checked" class="check-box" disabled="disabled" 
         type="checkbox" /></dd>
        <dd><input checked="checked" class="check-box" data-val="true" 
         data-val-required="bool型 フィールドが必要です。" id="BoolValue" name="BoolValue" 
         type="checkbox" value="true" />
         <input name="BoolValue" type="hidden" value="false" /></dd>
<dt>bool(Nullable)型</dd>
        <dd><select class="tri-state list-box" disabled="disabled">
         <option value="">設定なし</option>
         <option selected="selected" value="true">True</option>
         <option value="false">False</option>
         </select>
        </dd>
        <dd><select class="list-box tri-state" id="NullableBoolValue" name="NullableBoolValue">
         <option value="">設定なし</option>
   <option selected="selected" value="true">True</option>
   <option value="false">False</option>
   </select>
  </dd>
<dt>enum型</dd>
        <dd>rainy</dd>
        <dd><input class="text-box single-line" data-val="true" 
         data-val-required="enum型 フィールドが必要です。" id="EnumValue" name="EnumValue" 
         type="text" value="rainy" /></dd>
</dl>

2016年12月15日木曜日

ASP.NET aspxのインライン式

覚えられないので自分用にメモ
詳しくはhttps://support.microsoft.com/ja-jp/kb/976112
<% ~ %> コード ブロックを埋め込む ASPとの後方互換性を保持するための埋め込みコードブロック
<%= ~ %>式を表示 Response.Write(...) で代用できる埋め込みコードブロック。
文字列などを表示するもっとも簡単な方法。
<%:= ~ %>式を表示(HTMLエンコード付
<%@ ~ %>ディレクティブ ページの設定を行う aspxページの設定を指定する構文
<%# ~ %>データバインディング式 RepeaterコントロールなどでDataBindしている場合に使用する。
<%# Eval("hoge") %>
<%$ ~ %>式ビルダー アプリケーション構成ファイルやリソース ファイルに含まれる情報に基づいて、コントロールのプロパティの値を設定する。
<%-- ~ --%>サーバー側コメント ブロック

2016年12月11日日曜日

ASP.NET MVC 12_HTMLヘルパー ~その4~

VisualStuidioCommunity2015/Fw4.5.2/C#


前回に続いて基本的なHTMLヘルパーのうち、今回は下記のヘルパー見ていきます。
  • DropDownList/ DropDownListFor
  • ListBox / ListBoxFor
  • EnumDropDownListFor

入力要素のレンダリング


DropDownList/ DropDownListFor

まずはDropDownListです。

ドロップダウンリストの選択肢リストの作成にはIEnumerable<SelectListItem>を使用する方法とSelectListを使用する方法があります。
下記のサンプルはIEnumerable<SelectListItem>を使用した方法です。
選択肢の作成と同時に選択状態を表すselectedプロパティを指定します。
ビューで選択肢リストを作成していますが、コントローラーで作成しViewBag経由で引き渡してもいいですし、ビューモデル経由で引き渡してもいいです。
@{ 
    var items= new List<SelectListItem>()
        {
            new SelectListItem() {Value = "1", Text = "日曜日" },
            new SelectListItem() {Value = "2", Text = "月曜日" },
            new SelectListItem() {Value = "3", Text = "火曜日" , Selected = true},
            new SelectListItem() {Value = "4", Text = "水曜日" },
            new SelectListItem() {Value = "5", Text = "木曜日" },
            new SelectListItem() {Value = "6", Text = "金曜日" },
            new SelectListItem() {Value = "7", Text = "土曜日" },
        };
}
DropDownList:
@Html.DropDownList("DropDownListID", items)
DropDownList(selectListitem)の出力:
<select id="DropDownListID" name="DropDownListID">
<option value="1">日曜日</option>
<option value="2">月曜日</option>
<option selected="selected" value="3">火曜日</option>
<option value="4">水曜日</option>
<option value="5">木曜日</option>
<option value="6">金曜日</option>
<option value="7">土曜日</option>
</select>
次にSelectListを使用する方法です。
SelectListは1つの項目を選択できる一覧を表すクラスです。
IEnumerable<SelectListItem>を実装しています。

まず選択肢リストになるIEnumerableなリストを作成します。
そして選択肢リスト、選択値用フィールド名、表示用フィールド名、および選択値を指定して、SelectListインスタンスを作ります。
DropDownListメソッドには作成したSelectListを指定します。
@{

    var items = new List<KeyValuePair<string,string>>()
        {
            new KeyValuePair<string,string>("1", "日曜日"),
            new KeyValuePair<string,string>("2", "月曜日"),
            new KeyValuePair<string,string>("3", "火曜日"),
            new KeyValuePair<string,string>("4", "水曜日"),
            new KeyValuePair<string,string>("5", "木曜日"),
            new KeyValuePair<string,string>("6", "金曜日"),
            new KeyValuePair<string,string>("7", "土曜日"),
        };

    var list = new SelectList(items,    //選択肢リスト
                        "Key",          //Value値に指定するプロパティ名
                        "Value",        //Text値に指定するプロパティ名
                        "4");           //選択値
}
DropDownList(SelectList):
@Html.DropDownList("DropDownListID", list)

続いてDropDownListForです。
こちらはIEnumerable<SelectListItem>を使用しても、選択値はモデルから設定できます。
@{
    var items = new List<SelectListItem>()
        {
            new SelectListItem() {Value = "1", Text = "日曜日" },
            new SelectListItem() {Value = "2", Text = "月曜日" },
            new SelectListItem() {Value = "3", Text = "火曜日" },
            new SelectListItem() {Value = "4", Text = "水曜日" },
            new SelectListItem() {Value = "5", Text = "木曜日" },
            new SelectListItem() {Value = "6", Text = "金曜日" },
            new SelectListItem() {Value = "7", Text = "土曜日" },
        };
}
DropDownListFor:
@Html.DropDownListFor(mdl => mdl.DropDownValue, items)
SelectListを使用した方法です。
@{ 
    var items = new List<KeyValuePair<string, string>>()
            {
                new KeyValuePair<string,string>("1", "日曜日"),
                new KeyValuePair<string,string>("2", "月曜日"),
                new KeyValuePair<string,string>("3", "火曜日"),
                new KeyValuePair<string,string>("4", "水曜日"),
                new KeyValuePair<string,string>("5", "木曜日"),
                new KeyValuePair<string,string>("6", "金曜日"),
                new KeyValuePair<string,string>("7", "土曜日"),
            };

    var list = new SelectList(items,"Key","Value"); 
}
@Html.DropDownListFor(mdl => mdl.DropDownValue, list)

ListBox/ ListBoxFor

複数選択できるリストボックスです。
ドロップダウンリストの選択肢リストの作成にはIEnumerable<SelectListItem>を使用する方法とMultiSelectListを使用する方法があります。

まずはListBoxです。
下記のサンプルはIEnumerable<SelectListItem>を使用した方法です。
@{ 
    var items= new List<SelectListItem>()
        {
            new SelectListItem() {Value = "1", Text = "日曜日" },
            new SelectListItem() {Value = "2", Text = "月曜日" },
            new SelectListItem() {Value = "3", Text = "火曜日" , Selected = true},
            new SelectListItem() {Value = "4", Text = "水曜日" },
            new SelectListItem() {Value = "5", Text = "木曜日" },
            new SelectListItem() {Value = "6", Text = "金曜日" },
            new SelectListItem() {Value = "7", Text = "土曜日" , Selected = true},
        };
}
ListBox:
@Html.ListBox("ListBoxID", items)
ListBoxの出力:
<select id="ListBoxID" multiple="multiple" name="ListBoxID">
<option value="1">日曜日</option>
<option value="2">月曜日</option>
<option selected="selected" value="3">火曜日</option>
<option value="4">水曜日</option>
<option value="5">木曜日</option>
<option selected="selected" value="6">金曜日</option>
<option value="7">土曜日</option>
</select>
次にMultiSelectListを使用する方法です。
MultiSelectListは複数の項目を選択できる一覧を表すクラスです。
IEnumerable<SelectListItem>を実装しています。
{
    var items = new List<KeyValuePair<string, string>>()
        {
            new KeyValuePair<string,string>("1", "日曜日"),
            new KeyValuePair<string,string>("2", "月曜日"),
            new KeyValuePair<string,string>("3", "火曜日"),
            new KeyValuePair<string,string>("4", "水曜日"),
            new KeyValuePair<string,string>("5", "木曜日"),
            new KeyValuePair<string,string>("6", "金曜日"),
            new KeyValuePair<string,string>("7", "土曜日"),
        };
    var selectedItems = new String[] { "3", "6" };

    var list= new MultiSelectList (items, "Key", "Value", selectedItems);
}
ListBox:
@Html.ListBox("ListBoxID", list)
つづいてListBoxForです。
IEnumerable<SelectListItem>を使用する例です。
ビューモデル
public class HtmlHelperViewModels
{
    public string[] ListBoxValues { get; set; }
}
コントローラー
public class HtmlHelperController : Controller
{
    public ActionResult Index()
    {
        var mdl = new Models.HtmlHelperViewModels();
        mdl.ListBoxValues = new string[] { "3", "6" };
        return View(mdl);
    }
}
ビュー
@{
    var items = new List<SelectListItem>()
        {
            new SelectListItem() {Value = "1", Text = "日曜日" },
            new SelectListItem() {Value = "2", Text = "月曜日" },
            new SelectListItem() {Value = "3", Text = "火曜日" },
            new SelectListItem() {Value = "4", Text = "水曜日" },
            new SelectListItem() {Value = "5", Text = "木曜日" },
            new SelectListItem() {Value = "6", Text = "金曜日" },
            new SelectListItem() {Value = "7", Text = "土曜日" },
        };
}
ListBoxFor:
@Html.ListBoxFor(mdl => mdl.ListBoxValues, items)
次にMultiSelectListを使用する方法です。
@{
    var items = new List<KeyValuePair<string, string<<()
        {
            new KeyValuePair<string,string>("1", "日曜日"),
            new KeyValuePair<string,string>("2", "月曜日"),
            new KeyValuePair<string,string>("3", "火曜日"),
            new KeyValuePair<string,string>("4", "水曜日"),
            new KeyValuePair<string,string>("5", "木曜日"),
            new KeyValuePair<string,string>("6", "金曜日"),
            new KeyValuePair<string,string>("7", "土曜日"),
        };
    var selectedItems = new String[] { "3", "6" };
    var list = new MultiSelectList(items, "Key", "Value", selectedItems);
}
ListBoxFor(SelectList):
@Html.ListBoxFor(mdl => mdl.ListBoxValues , list)

EnumDropDownListFor

ASP.NET MVC 5.1 からは列挙帯からDropDownListを生成することができます。
モデル
namespace Practice.Models
{
    public class HtmlHelperViewModels
    {
        public WeekdayType EnumValue { get; set; }
    }

    public enum WeekdayType
    {
        Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
    };
}
コントローラー
namespace Practice.Controllers
{
    public class HtmlHelperController : Controller
    {

        public ActionResult Index()
        {
            Models.HtmlHelperViewModels mdl = new Models.HtmlHelperViewModels();
            mdl.EnumValue = Models.WeekdayType.Thursday;
            return View(mdl);
        }
    }
}
ビュー
@Html.EnumDropDownListFor(mdl => mdl.EnumValue)
EnumDropDownListForの出力: 
<select data-val="true" data-val-required="EnumValue フィールドが必要です。" id="EnumValue" name="EnumValue">
<option value="0">Monday</option>
<option value="1">Tuesday</option>
<option value="2">Wednesday</option>
<option selected="selected" value="3">Thursday</option>
<option value="4">Friday</option>
<option value="5">Saturday</option>
<option value="6">Sunday</option>
</select>

EnumHelper.GetSelectListメソッドを使用すると、列挙帯からIList<SelectListItem>オブジェクトを生成することもできます。
@{ 
    IList<SelectListItem> enumList = 
            EnumHelper.GetSelectList(typeof(Practice.Models.WeekdayType));       
}
DropDownListFor:
@Html.DropDownListFor(mdl => mdl.EnumDropDownValue, enumList)

DropDownList:
@Html.DropDownList("EnumDropDownListID", new SelectList(enumList,"Value","Text","2"))
DropDownListの表示する値を列挙子の名前ではなく、他の表示名にしたい場合は、列挙子にDisplay属性で表示名を指定します。
using System.ComponentModel.DataAnnotations;
・・・省略・・・
public enum WeekdayType
{
    [Display(Name = "月曜")]
    Monday,
    [Display(Name = "火曜")]
    Tuesday,
    [Display(Name = "水曜")]
    Wednesday,
    [Display(Name = "木曜")]
    Thursday,
    [Display(Name = "金曜")]
    Friday,
    [Display(Name = "土曜")]
    Saturday,
    [Display(Name = "日曜")]
    Sunday
};

2016年12月10日土曜日

ASP.NET MVC 12_HTMLヘルパー ~その3~

VisualStuidioCommunity2015/Fw4.5.2/C#


前回に続いて基本的なHTMLヘルパーのうち、今回は下記のヘルパー見ていきます。
  • RadioButton/ RadioButtonFor
  • CheckBox/ CheckBoxFor

入力要素のレンダリング


RadioButton/ RadioButtonFor

ラジオボタンのテキストは<label>タグで設定し、<label>タグでRadioButtonまたはRadioButtonForを囲みます。
ラジオボタンの選択肢の数だけ、<label>タグとRadioButtonまたはRadioButtonForを用意します。
RadioButtonメソッドは第一引数nameに指定するフィールド名をすべて同じ値にします。
RadioButtonForメソッドは第一引数に指定するモデルのプロパティをすべて同じにします。
RadioButton:



RadioButtonFor:


RadioButtonの出力:
<label><input id="RadioButtonID" name="RadioButtonID" type="radio" value="RadioValueA" /> RadioA</label>
<label><input checked="checked" id="RadioButtonID" name="RadioButtonID" type="radio" value="RadioValueB" /> RadioB</label>

RadioButtonForの出力:
<label><input id="RadioValue" name="RadioValue" type="radio" value="RadioValueA" /> RadioA</label>
<label><input checked="checked" id="RadioValue" name="RadioValue" type="radio" value="RadioValueB" /> RadioB</label>
出力されたhtmlを見るとname属性とid属性がすべて同じ値になっています。
idを分けたい場合は属性で指定します。
RadioButton:



RadioButtonFor:



CheckBox/ CheckBoxFor

チェックボックスのテキストは<label>タグで設定し、<label>タグでCheckBoxまたはCheckBoxForを囲みます。
チェックボックスの選択肢の数だけ、<label>タグとCheckBoxまたはCheckBoxForを用意します。
ラジオボタンと異なり、 CheckBoxメソッドは第一引数nameに指定するフィールド名をそれぞれ別の値にします。
CheckBoxForメソッドは第一引数に指定するモデルのプロパティはそれぞれのチェック状態を表すモデルのプロパティにします。
CheckBox:




CheckBoxFor:



CheckBoxの出力(Aだけ記載):
<label>
<input checked="checked" id="CheckBoxID_A" name="CheckBoxID_A" type="checkbox" value="true" />
<input name="CheckBoxID_A" type="hidden" value="false" />
 CheckA</label>

CheckBoxForの出力(Aだけ記載):
<label>
<input checked="checked" data-val="true" data-val-required="CheckBoxValueA フィールドが必要です。" 
id="CheckBoxValueA" name="CheckBoxValueA" type="checkbox" value="true" />
<input name="CheckBoxValueA" type="hidden" value="false" />
 CheckA</label>
出力内容を確認すると<input type="checked">と<input type="hidden">の2つの要素が出力されています。
チェックボックスがチェックされなかった時にも、チェックされなかったというfalse値をサーバーに送信するためです。
※CheckBoxForには「data-val-required」属性が出力されていますが、今は無視します。


ラジオボタンやチェックボックスの選択肢が多い場合、選択肢の数だけタグを書くのはメンドクサイですね。
Asp Web Form にあったCheckBoxListやRadioButtonListが欲しいところです。
毎回RadioButtonのidを属性で指定しなければいけないのもメンドクサイです。
そんなときは自作のHtmlヘルパーを作って対応します。
いずれ自作してみたいと思いますが、今は無視します。

2016年12月8日木曜日

ASP.NET MVC 11_HTMLヘルパー ~その2~

VisualStuidioCommunity2015/Fw4.5.2/C#


前回に続いて基本的なHTMLヘルパーのうち、今回は下記のヘルパー見ていきます。
  • Label / LabelFor
  • TextBox / TextBoxFor
  • TextArea / TextAreaFor
  • Password / PasswordFor
  • Hidden/ HiddenFor

入力要素のレンダリング

TextBodForやLabelForなどの「For付き」メソッドはモデル関連付いたフォーム要素を生成します。
TextBoxやLabelなどの「Forなし」メソッドはモデルに関連付かないフォーム要素を生成します。

属性にreadonlyやclassなどのC#の予約語を使用する場合は、「@」を付けます。

Label / LabelFor

Labelメソッドは引数に指定した文字列を出力します。
LabelForは引数に指定したモデルのプロパティ名を表示します。
モデルのプロパティの値を表示するわけではありませんので注意してください。

コントローラー
public ActionResult Index()
{
    Models.HtmlHelperViewModels mdl = new Models.HtmlHelperViewModels();
    mdl.LabelText = "LabelTextValue";
    return View(mdl);        
}
ビュー
Label:
@Html.Label("LabelTextValue")

LabelFor:
@Html.LabelFor(mdl => mdl.LabelText)
Labelの出力:
<label for="LabelTextValue">LabelTextValue</label>

LabelForの出力:
値「LabelTextValue」ではなくプロパティ名「Labeltext」が出力されている
<label for="LabelText">LabelText</label>

LabelForで表示するプロパティ名はDisplay属性やDisplayName属性を使用してカスタマイズすることができます。
//DisplayName属性を使用する場合にインポートする
using System.ComponentModel;
//Display属性を使用する場合にインポートする
using System.ComponentModel.DataAnnotations;

namespace Practice.Models
{
    public class HtmlHelperViewModels
    {
        [DisplayName("表示名1")]
        public string LabelText1{ get; set; }

        [Display(Name = "表示名")]
        public string LabelText2{ get; set; }
    }
}

モデルのプロパティの値をラベルで出力したい場合は、Labelメソッドを使用します。
Label:
@Html.Label(Model.LabelText)

TextBox / TextBoxFor

TextBox:
@Html.TextBox("TextBoxID", "TextBoxValue", new { @readonly = "readonly", size = "20", maxlength = 40 })

TextBoxFor:
@Html.TextBoxFor(mdl => mdl.TextBoxText, new { @readonly = "readonly", size = "20", maxlength = 40 })
TextBoxの出力:
<input id="TextBoxID" maxlength="40" name="TextBoxID" readonly="readonly" size="20" type="text" value="TextBoxValue" />

TextBoxForの出力:
<input id="TextBoxText" maxlength="40" name="TextBoxText" readonly="readonly" size="20" type="text" value="TextBoxTextValue" />

TextArea / TextAreaFor

TextArea:
@Html.TextArea("TextAreaID",                    //要素名
                "TextAreaValue",                //値
                5,                              //行数
                50,                             //桁数
                new { @class = "multiline" }    //属性
                )

TextAreaFor:
@Html.TextAreaFor(mdl => mdl.TextAreaText, 5, 50, new { @class = "multiline" })
TextAreaの出力:
<textarea class="multiline" cols="50" id="TextAreaID" name="TextAreaID" rows="5">
TextAreaValue</textarea>

TextAreaForの出力:
<textarea class="multiline" cols="50" id="TextAreaText" name="TextAreaText" rows="5">
TextAreaTextValue</textarea>

Password / PasswordFor

Password:
@Html.Password("PasswordID", "PasswordText", new { size = 10, maxlength = 20 })

PasswordFor:
@Html.PasswordFor(mdl => mdl.PasswordText, new { size = 10, maxlength = 20 })
Passwordの出力:
<input id="PasswordID" maxlength="20" name="PasswordID" size="10" type="password" value="PasswordText" />

PasswordForの出力:
<input id="PasswordText" maxlength="20" name="PasswordText" size="10" type="password" />
PasswordForの出力を見るとValueプロパティが設定されていません。
ASP.NET Web Formでも同様でしたがPasswordテキストボックスには値が表示できません。
値を表示させるにはvalue属性を使用しますが、ソースの表示でパスワードの値が丸見えになることに注意してください。
@Html.PasswordFor(mdl => mdl.PasswordText, new { size = 10, maxlength = 20, value = Model.PasswordText })
<input id="PasswordText" maxlength="20" name="PasswordText" size="10" type="password" value="PasswordTextValue" />

Hidden / HiddenFor

Hidden:
@Html.Hidden("HiddenID", "HiddenValue")

HiddenFor:
@Html.HiddenFor(mdl => mdl.HiddenValue)
Hiddenの出力:
<input id="HiddenID" name="HiddenID" type="hidden" value="HiddenValue" />

HiddenForの出力:
<input id="HiddenValue" name="HiddenValue" type="hidden" value="HiddenValue" />

ASP.NET MVC 10_HTMLヘルパー ~その1~

VisualStuidioCommunity2015/Fw4.5.2/C#


ASP.NET Web Formsではサーバーコントロールがありました。
ASP.NET MVCではサーバーコントロールにかわって、HTMLヘルパーを使用しコントロールをレンダリングします。

基本的なHTMLヘルパー

  • BeginForm / BeginRouteForm
  • EndForm
  • Label / LabelFor
  • TextBox / TextBoxFor
  • TextArea / TextAreaFor
  • Password / PasswordFor
  • Hidden/ HiddenFor
  • RadioButton/ RadioButtonFor
  • CheckBox/ CheckBoxFor
  • DropDownList/ DropDownListFor
  • ListBox / ListBoxFor
  • EnumDropDownListFor

HTMLフォームのレンダリング

BeginForm 、EndForm

BeginFormメソッドは<form>タグを生成します。
EndFormメソッドは</form>タグを生成します。
@Html.BegionForm()
    ・・・フォームの内容(省略)
@Html.EndForm()
BeginFormメソッドはusing構文を使用することもできます。
using構文を使用すると、ブロックの終了で</form>タグを生成します。
@using (Html.BegionForm())
{
    ・・・フォームの内容(省略)
}
BeginFormメソッドの引数をまったく指定しない場合、現在のアクションメソッドにPostします。
BeginFormメソッドにはいろいろなオーバーロードが用意されています。
主な引数を指定した場合の例です。
@{
    //アクション名
    string actionName = "Edit";
    //コントローラー名
    string controllerName = "HtmlHelper";
    //アクションメソッドへの引数
    object routeValues = new { id ="1",otherParam ="xxx"};
    //HTTPメソッド(GET/POST)
    FormMethod frmMethod = FormMethod.Post;
}
   
@using (Html.BeginForm(actionName, controllerName, routeValues, frmMethod))
{
    ・・・フォームの内容(省略)
}
「<form action="/helper/HtmlHelper/Edit/1?otherParam=xxx" method="post">」と生成されます。


BeginRouteForm

BeginRouteFormメソッドを指定すると、ルート定義ファイルで設定されているルート名へ送信するフォームを生成できます。
ルート定義についてはコチラ ASP.NET MVC 04_ルーティングの基礎

ルート定義ファイルの内容です。
App_Start/RouteConfig.cs
routes.MapRoute(
    name: "HtmlHelper",
    url: "helper/sample/{controller}/{action}/{id}",
    defaults: new { controller = "HtmlHelperSample", action = "Edit", id = UrlParameter.Optional }
);
以下の例では、ルート名「HtmlHelper」、コントローラー「現在のコントローラー名」、アクション「現在のアクションメソッド名」に送信されるformタグが生成されます。
ルート定義のデフォルトコントローラーのデフォルトアクションではありません。
現在HelloControllerのWorldアクションを実行していれば、
「<form action="/helper/sample/Hello/World" method="post">」と生成されます。
@using (Html.BegionRouteForm("HtmlHelper"))
{
    ・・・フォームの内容(省略)
]

コントローラ名やアクションを指定する場合、匿名型のオブジェクトとして指定します。
@{ 
    //ルート名
    string routeName = "HtmlHelperSample";
    //ルートパラメータ
    object routeValues = new
    {
        Controller = "HtmlHelperSample",
        Action = "Edit",
        id = 1,
        otherParam = "xxx"
    };
    //HTTPメソッド(GET/POST)
    FormMethod frmMethod = FormMethod.Post;
}

@using (Html.BeginRouteForm(routeName, routeValues, frmMethod))
{
    ・・・フォームの内容(省略)
}
「<form action="/helper/sample/HtmlHelperSample/Edit/1?otherParam=xxx" method="post"> 」と生成されます。

ASP.NET MVC 09_Razorビューエンジン ~Razor構文 その2~

VisualStuidioCommunity2015/Fw4.5.2/C#


前回に続いてRazorの構文を見ていきます。

「@using」で名前空間をインポート

Razorは標準でだいたいの名前空間をインポートしてくれますが、インポートされない名前空間は「@using」でインポートすることができます。
@using MyApp.MyExtensions;

@{ 
    bool ret = string.Empty.MyExtensionMethod();
}

「@if」コードブロックで条件分岐

コードブロック内ではifを使用できますが、コードブロック外でifを使用したい場合は「@if」コードブロックを使用します。
@{ int num = new System.Random().Next(1, 10); }

@if (num % 2 == 0)
{
    @:偶数
}
else
{
    @:奇数
}

「@switch」コードブロックで条件分岐

@switch (num)
{
    case 2:
        @: numは2です。
        break;
    default:
        @: numは2以外です。
        break;
}

「@for」コードブロックで繰り返し処理

@{ int max = 10; }
@for (int i = 0; i < max; i++)
{
    

@i

}

「@foreach」コードブロックで繰り返し処理

@{ int[] nums = { 2, 4, 6 }; } 
@foreach (int val in nums)
{
    

@val

}

「@while」コードブロックで繰り返し処理

@{ int counter = 1; }
@while (counter < 10 )
{
    

@counter

counter++; }

「@functions」コードブロックでメソッドやプロパティを定義

「@functions」コードブロック内ではメソッドやプロパティを定義できます。
@functions
{
    // メソッド定義
    bool isEven(int num)
    {
        bool ret = false;
        if (num % 2 == 0)
        {
            ret = true;
        }
        return ret;
    }

    //プロパティ定義
    string _str = "Hello";
    String Str
    {
        get { return _str + " World"; }
        set { _str = value; }
    }
}

8は偶数:@isEven(8)

プロパティの値:@_str / @Str

通常のコードブロック内でメソッドを定義したければ、匿名メソッド使えばよさそうです。
@{ 
    Func<int, bool> isOdd = (val) =>
    {
        bool ret = false;
        if (num % 2 != 0)
        {
            ret = true;
        }
        return ret;
    };
}

8は奇数:@isOdd(8)


条件付きナゲット (ASP.NET MVC5 以降)

コードナゲットを属性に使用すると、式が「trueである」又は「nullでない」場合にのみ属性が生成されます。

たとえば下記の例であれば、
式「styleRed」はnullではないので、style属性は出力されます。
式「styleNull」はnullなので、style属性自体が出力されません。
@{ string styleRed = "color:red"; }

style = color:red

@{ string styleNull = null; }

style = null

実行してソースの表示で出力されたHTMLを確認した結果です。

style = color:red

style = null

2016年12月6日火曜日

ASP.NET MVC 08_Razorビューエンジン ~Razor構文 その1~

VisualStuidioCommunity2015/Fw4.5.2/C#


今回はビューエンジンについてです。
ビューエンジンはコントローラーから渡されたデータと、ビューの定義ファイル(cshtmlファイル)を組み合わせ、
サーバー側でレンダリング(HTMLコードに変換)する処理を行います。

ASP.NET MVC で使用できる代表的なビューエンジンは
ASP.NET Webフォームで使用していたASPXエンジンと、ASP.NET MVC3で新たに登場したRazorエンジンがあります。

ASP.NET MVC4ではプロジェクトを作成する時に、ASPXエンジンかRazorエンジンのどちらを使用するか選択できました。
ASP.NET MVC5では選択することができなくなり、標準でRazorビューエンジンが使用されます。


コードナゲット(インライン式)

ビューテンプレート(ビュー定義ファイル)にコードを埋め込んだ部分をコードナゲットと呼びます。
ASPXエンジンでは<%・・・%>と書いていた部分です。
Razorでは「@・・・」の1文字で始まり、閉じる必要がありません。コードナゲットの終わりは自動的に判定されます。

たとえば以下のコードでは「Model.Message」を式とみなします。
実行するとモデルのMessageプロパティの内容が出力されます。

@Model.Message


明示的コードナゲット

Razorでは式の終わりを自動的に判定しますが、明示的に式の範囲を指定したい時もあります。
式の範囲を指定する場合「@(・・・)」と丸かっこで囲みます。

たとえば次の例ではモデルのUserNameプロパティの内容に、静的なコンテンツである「さん」を結合して出力したいとします。
以下のように書くと、モデルに「UserNameさん」プロパティが定義されていないとコンパイルエラーになっていまいます。

@Model.UserNameさん

こんなときは「@(…)」を使用して、明示的にインライン式の範囲を指定します。
以下のコードではModel.UserNameまでを式とみなし、UserNameプロパティの内容に「さん」を結合して出力します。

@(Model.UserName)さん

次の例えはモデルのPriceプロパティの内容(1000)に1.08を掛けて「1080」と出力しようとしたものとします。
しかしPriceプロパティの後の空白が式の終わりと判定されてしまうため、「1000 * 1.08」と出力されてしまいます。

@Model.Price * 1.08

そこで「1080」と出力するために、式の終わりをは明示的に指定します。

@(Model.Price * 1.08)


@のエスケープ

ほとんどの場合、Razorは@がコードナゲットを表しているのかどうかを正しく判定します。
それでも文字列として出力したい@がコードナゲットとして認識されてしまう場合は、@を@@と2重にしてエスケープします。

@@Model.UserNameはモデルのプロパティです。


コードブロック

複数の文で構成されるコード・ブロックを記述したい場合は、「@{・・・}」と波括弧で囲みます。

以下の例ではコードブロック内で変数msgを定義し、コードブロック終了後のコードナゲットで変数msgの内容を出力します。
@{
    string msg = "Hello World";
}

@msg


コードブロック内で変数や文字列を出力したい場合は下記のようにタグで囲みます。
@{ 
    String ptn1 = "パターン1";
    

htmlタグで囲んで: @ptn1

}
タグで囲みたくない場合などは、先頭に「@:」を付けるとその行は静的コンテンツとして出力されます。
@{
    String ptn2 = "パターン2";
    @:複数行で出力するなら @ptn2
}
静的コンテンツが複数行になる場合は、下記のように<text>タグで囲みます。
<text>タグはRazorに静的コンテンツと知らせるためだけのダミータグで、<text>タグ自体は何も出力しません。
@{
    String ptn3 = "パターン3";
    
        複数行で出力するなら
        @ptn3
    
}
コードブロック内ではC#のコードが書けます。
@{
    //コメント     
    int number = new System.Random().Next(1, 10);
    if (number % 2 == 0)
    {
        @:number「@number」は偶数
    }
    else
    {
        @:number「@number」は奇数
    }
}

コメント

Razorでは「@*・・・*@」で囲まれた部分はコメントとみなされます。
HTMLのコメント「<!--・・・-->」は「ソースの表示」で表示されますが、Razorコメントは表示されません。
@*
    ここからはコメントです。

    @Model.Message

    @{
        int number = new System.Random().Next(1, 10);
    }
    ここまではコメントです。
*@