2018年9月15日土曜日

ASP.NET MVC 自作カスタム属性を、自作Htmlヘルパーでビューに表示する

VisualStudio2017 / ASP.NET MVC 5 / Framework4.6.2 / C#


ビューモデルのプロパティに指定した「Placeholder」カスタム属性の値を、「PlaceHolderFor」カスタムヘルパーでビューに出力できるようにします。

目指すゴールは
ビューモデルのプロパティにPlaceholder属性を設定し、プレースホルダーに表示する値を指定できるようにします。
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using MyApp.Addon.Attributes;

namespace MyApp.ViewModels
{
    /// 
    /// ログイン ビューモデル
    /// 
    public class Login
    {
        [DisplayName("ID"),
         Placeholder("IDを入力してください")]
        public string ID { get; set; }
    }
} 

次にビューでは @Html.PlaceholderFor ヘルパーを使用して、プレースホルダーの値を表示できるようにします。
@Html.EditorFor(mdl => mdl.ID, new { htmlAttributes = new { @class = "form-control", placeholder = @Html.PlaceholderFor(mdl => mdl.ID) } })

カスタム属性の作成

まずはPlaceHolder属性から作成していきます。
Placeholder属性では、プレースホルダーに表示する値をコンストラクタの引数で指定するようにしています。
モデルのプロパティに対して使用する属性なので、クラスに System.AttributeUsage(AttributeTargets.Property) 属性を指定します。
using System;

namespace MyApp.Addon.Attributes
{
    /// 
    /// プレースホルダー属性
    /// 
    [System.AttributeUsage(AttributeTargets.Property)]
    public class PlaceholderAttribute : System.Attribute 
    {
        /// 
        /// プレースホルダーとして表示値する値
        /// 
        public string DisplayValue { get; set; }

        /// 
        /// コンストラクタ
        /// 
        /// プレースホルダーとして表示する値
        public PlaceholderAttribute(string displayValue)
        {
            DisplayValue = displayValue;
        }
    }
}
次に属性を読み取るモデルメタデータプロバイダーを作成します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;


namespace MyApp.Addon.Attributes
{
    /// 
    /// 拡張したモデルメタデータプロバイダー
    /// 
    /// 
    /// Global.asaxのApplication_Start()で、モデルプロバイダーに指定する。
    /// 
    public class ModelMetadataProvidersEx : DataAnnotationsModelMetadataProvider
    {
        /// 
        /// 基底のCreateMetadataをオーバーライド
        /// 指定したモデルのメタデータを作成します。
        /// 
        /// 属性
        /// コンテナーの型。コンテナーが存在しない場合は null。
        /// モデル アクセサー。
        /// モデルの型。
        /// プロパティ名。モデルがプロパティではない場合は、null。
        /// モデルのメタデータ
        protected override ModelMetadata CreateMetadata(IEnumerable attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
        {
            //元のCreateMetadataメソッドを呼び出し
            ModelMetadata metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
            //Placeholder属性を追加する
            PlaceholderAttribute pha = attributes.OfType<PlaceholderAttribute>().FirstOrDefault();
            if (pha != null)
            {
                metadata.AdditionalValues.Add("Placeholder", pha);
            }
            
            return metadata;
        }
    }
}
作成したモデルメタデータプロバイダーを、Grobal.asaxのApplication_Startメソッドで、属性の読み取りに使用する設定を行います。
using System.Web.Mvc;
using System.Web.Routing;

namespace MyApp
{
    /// 
    /// アプリケーションイベント
    /// 
    public class MvcApplication : System.Web.HttpApplication
    {
        /// 
        /// アプリケーション起動時
        /// 
        protected void Application_Start()
        {
            //エリア登録
            AreaRegistration.RegisterAllAreas();
            //ルート登録
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            //データアノテーション属性の読み込みに、カスタマイズしたプロバイダーを使用する
            ModelMetadataProviders.Current = new MyApp.Addon.Attributes.ModelMetadataProvidersEx();
        }
        
    }
}
以上で属性の作成は終了です。
リビルドしたら、ビューモデルのプロパティに対して、プレースホルダーの属性を設定します。
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using MyApp.Addon.Attributes;

namespace MyApp.ViewModels
{
    /// 
    /// ログイン ビューモデル
    /// 
    public class Login
    {
        [DisplayName("ID"),
         Placeholder("IDを入力してください")]
        public string ID { get; set; }
    }
} 

カスタムHtmlヘルパーの作成

次にプレースホルダーの属性値を表示するためのHtmlヘルパーを作成します。
using System;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
using System.Dynamic;

using MyApp.Addon.Attributes;


namespace MyApp.Addon.Extentions
{
    /// 
    /// HTMLヘルパーに対する拡張クラス
    /// 
    public static class HtmlHelperEx
    {
       
        /// 
        /// PlaceholderFor プレースホルダー属性に指定された値を出力するHtmlヘルパー
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static IHtmlString PlaceholderFor<TModel, TValue>(
                             this HtmlHelper<TModel> htmlHelper,
                             Expression<Func<TModel, TValue>> expression)
        {

            var attrList = ModelMetadata.FromLambdaExpression<TModel, TValue>(expression, htmlHelper.ViewData);
            //プレースホルダーの属性値を返す
            if (attrList.AdditionalValues.ContainsKey("Placeholder"))
            {
                var plhAttr = (PlaceholderAttribute)attrList.AdditionalValues["Placeholder"];
                return new HtmlString(plhAttr.DisplayValue);
            }
            return new HtmlString("");
        }

        

    }
}

以上でヘルパーの作成は終了です。


よく使用するヘルパーであれば、Views/Web.configのnamespace要素に追加すると、各ビューにインポートを書かなくて済みます。
web.configに追加した場合、私の環境ではVisualStudioを再起動しないと、ビューで使用する際にコンパイルエラーになりました。

        
       ・・・省略
        

web.configに追加しない場合は、各ビューの先頭でインポートしてください。
@using MyApp.Addon.Extentions;
これでビューでは @Html.PlaceholderFor ヘルパーを使用して、プレースホルダーの値を表示できるようになります。
@Html.EditorFor(mdl => mdl.ID, new { htmlAttributes = new { @class = "form-control", placeholder = @Html.PlaceholderFor(mdl => mdl.ID) } })

実行結果

0 件のコメント: