SYSTEM,WEB,MOBILE and DESIGN
ASP.NET MVC

 

inputタグに属性を追加するための自作属性「HtmlProperties」を作ってみました。

maxlength属性や、onkeyup等のイベントハンドラをメタデータクラスで設定できるようにします。

 

[Required]
[HtmlProperties(maxlength = 64, onkeyup = "AjaxCheck('UserName',value);")]
[DisplayName("氏名")]
[DataType(DataType.Text)]
public string UserName { get; set; }

 

こんな感じで使えるようにするのが目標です。

 

なお、作成にあたって以下のサイトが大変参考になりましたm(_ _)m

Kiran Chand’s Blog : Adding html attributes support for Templates – ASP.Net MVC 2.0 Beta:

 

まず、属性クラスの本体を作成します。

 

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class HtmlPropertiesAttribute : Attribute
{
    public int maxlength { get; set; }
    public string onkeyup { get; set; }

    public IDictionary<string, object> HtmlAttributes()
    {
        IDictionary<string, object> htmlatts = new Dictionary<string, object>();

        if (maxlength != 0)
        {
            htmlatts.Add("maxlength", maxlength);
        }

        if (!String.IsNullOrEmpty(onkeyup))
        {
            htmlatts.Add("onkeyup", onkeyup);
        }

        return htmlatts;
    }
}

 

属性の基本クラス Attribute を継承し、HtmlPropertiesAttribute クラスを作成し、
各プロパティにinputタグに設定したい属性を記載します。

ここでは、maxlength,onkeyupを指定しています。

 

次に、HtmlPropertiesAttributeのメタデータ属性追加処理を記述します。

public class MetadataProvider : DataAnnotationsModelMetadataProvider
{
	protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
	             Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
	{
	    var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

	    var additionalValues = attributes.OfType<HtmlPropertiesAttribute>().FirstOrDefault();

	    if (additionalValues != null)
	    {
	        metadata.AdditionalValues.Add("HtmlAttributes", additionalValues);
	    }

	    return metadata;
	}
}

 

DataAnnotationsModelMetadataProvider クラスは規定のMVCフレームワークにおけるメタデータの総締めで、
メタデータを取得する CreateMetadata メソッドの内容を、HtmlAttributet を追加するように書き換えたクラス
MetadataProvider を作成します。

 

上記のクラスを、Global.asaxの中で、以下のようにしてModelMetadataProviders.Currentに指定します。

 

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);

            ModelMetadataProviders.Current = new MetadataProvider();
        }

 

次に、ビュー側で属性を埋め込むためのHtmlヘルパーメソッドを作成します。

 

namespace WebProject.Helpers {
    public static class Extensions
    {
        public static IDictionary<string, object> GetHtmlAttribute(this HtmlHelper htmlHelper)
        {
            return GetHtmlAttribute(htmlHelper, new Dictionary<string, Object>());
        }

        public static IDictionary<string, object> GetHtmlAttribute(this HtmlHelper htmlHelper, IDictionary<string, Object> htmlAttributes)
        {
            var htmlPropertiesAttribute = new WebProject.Common.HtmlPropertiesAttribute();

            if (htmlHelper.ViewData.ModelMetadata.AdditionalValues.ContainsKey("HtmlAttributes"))
            {
                htmlPropertiesAttribute = (WebProject.Common.HtmlPropertiesAttribute)htmlHelper.
                                           ViewData.ModelMetadata.AdditionalValues["HtmlAttributes"];
            }

            var attributes = htmlPropertiesAttribute.HtmlAttributes();
            attributes.Add("class", htmlHelper.ViewData.ModelMetadata.ContainerType.Name +
                "_" + htmlHelper.ViewData.ModelMetadata.PropertyName);

            foreach (KeyValuePair<string, object> kp in htmlAttributes)
            {
                attributes.Add(kp.Key, kp.Value);
            }

            return attributes;
        }
    }
}

 

メタデータから取得した属性リストと、引数から渡された属性リストを合わせて返却します。

ついでに内部で class 属性も追加していますが、この辺りはお好みで変更して下さい。

 

以下はビュー側の処理です。

 

<%= Html.EditorFor(model => model.name, ViewData.TemplateInfo.FormattedModelValue) %>
<%= Html.EditorFor(model => model.name, ViewData.TemplateInfo.FormattedModelValue, new { @style = "width:5em" })) %>

 

まずEditorFor から対象の画面項目の種類を判別し、カスタムテンプレートを呼び出します。

DataType 属性で Text を指定していれば、View→Shared→EditorTemplatesフォルダ配下の Text.ascx を使用します。

以下はText.ascxの全コードです。

 

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%@ Import Namespace="WebProject.Helpers" %>

<%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, Html.GetHtmlAttribute(ViewData)) %>

 

GetHtmlAttribute によって属性が付加されます。

HtmlProperthisを使いたい画面項目種別(Text や Password 等)ごとに
カスタムテンプレートを作成しなければならないので注意して下さい。

 

なお、検証用の属性を作成したい場合は、デフォルトのMVC2プロジェクトに存在するAccountModel.csの
ValidatePasswordLengthAttributeクラスが参考になるかと思います。