ASP.NET MVC的Model元数据与Model模板:预定义模板

通过ModelMetadata表示的Model元数据的一个主要的作用在于为定义在HtmlHelper和HtmlHelper中的模板方法(这些模板方法包括Display/DisplayFor、Editor/EditorFor、DisplayForModel/EditForModel、Lable/LabelFor和DisplayText/DisplayTextFor等)提供用于最终生成HTML的元数据信息。在调用这些方法的时候,如果我们指定了一个具体的通过分部View定义的模板,或者对应的ModelMetadata的TemplateHint属性具有一个模板名称,会自动采用该模板来生成最终的HTML。如果没有指定模板名称,则会根据数据类型在预定义的目录下去寻找做模板的分部View。如果找不到,则会利用默认的模板进行HTML的呈现。为了让读者对模板具有一个大概的认识,我们来做一个简单的实例演示。[本文已经同步到《How ASP.NET MVC Works?》中]

目录
一、 实例演示:通过模板将布尔值显示为RadioButton
二、 预定义模板
    EmailAddress
    HiddenInput
    Html
    Text与String
    Url
    MultilineText
    Password
    Decimal
    Boolean
    Collection
    Object

一、 实例演示:通过模板将布尔值显示为RadioButton

在默认的情况下,不论是对于编辑模式还是显示模式,一个布尔类型的属性值总是以一个CheckBox的形式呈现出来。我们创建如下一个表示员工的类型Employee,它具有一个布尔类型的属性IsPartTime表示该员工是否为兼职。

   1: public class Employee
   2: {
   3:     [DisplayName("姓名")]
   4:     public string Name { get; set; }
   5:  
   6:     [DisplayName("部门")]
   7:     public string Department { get; set; }
   8:  
   9:     [DisplayName("是否兼职")]
  10:     public bool IsPartTime { get; set; }
  11: }

如果我们直接调用HtmlHelper的EditorForModel方法将一个Employee对象显示在某个将Employee类型作为Model的强类型View中,下图体现了默认的呈现效果。我们可以看到表示是否为兼职的IsPartTime属性对应着一个CheckBox。

ASP.NET MVC的Model元数据与Model模板:预定义模板_第1张图片

现在我们希望的是将所有布尔类型对象显示为两个RadioButton,具体的显示效果如下图所示。那么我们就可以通过创建一个Model类型为Boolean的View来创建一个模板,使之改变所有布尔类型对象的默认呈现效果。

ASP.NET MVC的Model元数据与Model模板:预定义模板_第2张图片

由于我们需要改变的是布尔类型对象在编辑模式下的呈现形式,所以我们需要将作为模板的分布View定义在EditorTemplates目录下,这个目录可以存在于Views/ Shared下,也可以存在于Views/{ControllerName}下。由于ASP.NET MVC是采用数据类型作为匹配条件来寻找对应的模板的,所以我们需要将分部模板View命名为Boolean。下面的代码片断体现了这个分部试图的整个定义,我们通过调用HtmlHelper的RadioButton方法将两个布尔值(True/False)映射为对应的RadioButton,并且采用

来布局。

   1: @model bool
   2: <table>
   3:     <tr>
   4:         <td>@Html.RadioButton("",true,Model)是td>
   5:         <td>@Html.RadioButton("",false,!Model)否td>
   6:     tr>
   7: table>

值得一提的是,我们没有指定RadioButton的名称,而是指定一个空字符串,Html本身会对其进行命名,而命名的依据就是本章介绍的核心:Model元数据。Employee的IspartTime属性呈现在界面上对应的HTML如下所示,我们可以看到两个类型为radio的元素的name被自动赋上了对应的属性名称。美中不足的是它们具有相同的ID,如果希望让ID具有唯一性,可以对模板进行更加细致的定制。

   1: <div class="editor-label"><label for="IsPartTime">是否兼职label>div>
   2: <div class="editor-field">
   3:     <table>
   4:         <tr>
   5:             <td><input checked="checked" id="IsPartTime" name="IsPartTime" type="radio" value="True" .../>td>
   6:             <td><input id="IsPartTime" name="IsPartTime" type="radio" value="False" />td>
   7:         tr>
   8:     table>
   9: div>

二、预定义模板

上面我们介绍如何通过View的方式创建模板进而控制某种数据类型或者某个目标元素最终在UI界面上的HTML呈现方式,实际上在ASP.NET MVC的内部还定义了一系列的预定义模板。当我们调用HtmlHelper/HtmlHelper的模板方法对Model或者Model的某个成员进行呈现的时候,系统会根据当前的呈现模式(显示模式和编辑模式)和Model元数据获取一个具体的模板(自定义模版或者预定义模版)。由于Model具有显示和编辑两种呈现模式,所以定义在ASP.NET MVC内部的默认模版分为这两种基本的类型。接下来我们就逐个介绍这些预定义模版以及最终的HTML呈现方式。

EmailAddress

该模板专门针对用于表示Email地址的字符串类型的数据成员,它将目标元素呈现为一个href属性具有“mailto:”前缀的链接()。由于该模板仅仅用于Email地址的显示,所以只在显示模式下有效,或者说ASP.NET MVC仅仅定义了基于显示模式的EmailAddress模板。为了演示数据在不同模板下的呈现方式,我们定义了如下一个简单的数据类型Model,我们通过在属性Foo上应用UIHintAttribute特性将模板名称设置为“EmailAddress”。

   1: public class Model
   2: {
   3:     [UIHint("EmailAddress")]
   4:     public string Foo { get; set; }
   5: }

然后在一个基于Model类型的强类型View中,我们通过调用HtmlHelper的DisplayFor方法将一个具体的Model对象的Foo属性以显示模式呈现出来。

   1: @model Model
   2: @Html.DisplayFor(m=>m.Foo)

如下的代码片断表示Model的Foo属性对应的HTML,我们可以看到它就是一个针对Email地址的连接。当我们点击该链接的时候,相应的Email编辑软件(比如Outlook)会被开启用于针对目标Email地址的邮件编辑。

   1: <a href="mailto:[email protected]">[email protected]a>

HiddenInput

关于默认模板HiddenInput我们不应该感到模式,前面介绍的HiddenInputAttribute特性就是将表示Model元数据的ModelMetadata对象的TemplateHint属性设置为HiddenInput。如果目标元素采用HiddenInput模板,在显示模式下内容会以文本的形式显示;编辑模式下不仅会以文本的方式显示其内容,还会生成一个对应的type属性为“hidden”的元素。如果表示Model元数据的ModelMetadata对象的HideSurroundingHtml属性为True(将应用在目标元素上的特性HiddenInputAttribute的DisplayValue属性设置为False),不论是显示模式还是编辑模式下显示的文本都将消失。

同样以上面定义的数据类型Model为例,我们通过在Foo属性上应用UIHintAttribute特性将模板名称设置为“HiddenInput”。

   1: public class Model
   2: {
   3:     [UIHint("HiddenInput")]
   4:     public string Foo { get; set; }
   5:     public bool Bar { get; set; }
   6:     public decimal Baz { get; set; }
   7: }

然后在一个基于Model类型的强类型View中分别调用HtmlHelper的DisplayFor和EditFor方法将一个具体的Model对象的Foo属性以显示和编辑模式呈现出来。

   1: @model Model
   2: @Html.DisplayFor(m=>m.Foo)
   3: @Html.EditorFor(m=>m.Foo)

分别以两种模式呈现出来的Foo属性对应的HTML如下(包含在花括号中的GUID表示属性值)。第一行是针对显示模式的,可以看出最终呈现出来仅限于表示属性值得文本;而编辑模式对应的HTML中不仅包含属性值文本,还具有一个对应的类型为“hidden”的元素。

   1: {42A1E9B7-2AED-4C8E-AB55-78813FC8C233} 
   2: {42A1E9B7-2AED-4C8E-AB55-78813FC8C233}"Foo" name="Foo" type="hidden" value="{42A1E9B7-2AED-4C8E-AB55-78813FC8C233}" />

现在我们对数据类型Model做一下简单修改,将应用在属性Foo上的UIHintAttribute特性替换成HiddenInputAttribute特性,并将其DisplayValue属性设置成False。

   1: public class Model
   2: {
   3:     [HiddenInput(DisplayValue = false)]
   4:     public string Foo { get; set; }
   5:     public bool Bar { get; set; }
   6:     public decimal Baz { get; set; }
   7: }

由于应用在目标元素上的HiddenInputAttribute特新的DisplayValue属性会最终控制对应ModelMetadata的HideSurroundingHtml属性,而后者控制是否需要生成用于显示目标内容的HTML。所以针对针对的Model定义,最终会生成如下一段HTML。

   1: <input id="Foo" name="Foo" type="hidden" value="{42A1E9B7-2AED-4C8E-AB55-78813FC8C233}" />

Html

如果目标对象的内容包含一些HTML,并需要在UI界面中原样呈现出来,我们可以采用Html模板。和EmailAddress模板一样,该模板仅限于显示模式。为了演示Html模板对目标内容的呈现方法与默认呈现方式之间的差异,我们定义了如下一个数据类型Model。该数据类型具有两个字符串类型的属性Foo和Bar,其中Foo上面应用UIHintAttribute特性将模板名称设置为“Html”。

   1: public class Model
   2: {
   3:     [UIHint("Html")]
   4:     public string Foo { get; set; }
   5:     public string Bar { get; set; }
   6: }

现在我们创建一个具体的Model对象,并将Foo和Bar设置为一段表示链接的文本(google.com),最终在一个基于Model类型的强类型View中通过调用HtmlHelper的DisplayFor方法将这两个属性以显示模式呈现出来。

   1: @model Model
   2: @Html.DisplayFor(m=>m.Foo)
   3: @Html.DisplayFor(m => m.Bar)

从如下所示的表示Foo和Bar两属性的HTML中我们不难看出:采用Html模板的Foo属性的内容原样输出,而包含在属性Bar中的HTML都进行了相应的编码。

   1: <a href="www.google.com">google.coma> <a href="www.google.com">google.com</a>

Text与String

不论是在显示模式还是编辑模式,Text和String模板具有相同的HTML呈现方式(实际上在ASP.NET MVC内部,两种模版最终生成的HTML是通过相同的方法产生的)。对于这两种模版来说,目标内容在显示模式下直接以文本的形式输出;而在编辑模式下则对应着一个单行的文本框

为了演示两种模版的同一性,我们对上面定义数据类型Model略作修改,在属性Foo和Bar上应用UIHintAttribute特性并将模版名称分别设置为String和Text。

   1: public class Model
   2: {
   3:     [UIHint("String")]
   4:     public string Foo { get; set; }
   5:     [UIHint("Text")]
   6:     public string Bar { get; set; }
   7: }

然后我们创建一个具体的Model对象,并在一个基于该Model类型的强类型View中通过调用HtmlHelper的DisplayFor和EditorFor将两个属性分别以显示和编辑模式呈现出来。

   1: @model Model
   2: @Html.DisplayFor(m=>m.Foo)
   3: @Html.DisplayFor(m => m.Bar)
   4: @Html.EditorFor(m=>m.Foo)
   5: @Html.EditorFor(m => m.Bar)

如下所示的代码片断体现了上述四个元素对应的HTML(“Dummy text …”是Foo和Bar的属性值),可以看到采用了Text和String模板的两个属性在显示和编辑模式下具有相同的呈现方式。编辑模式下输出的类型为“text”的元素表示CSS特性类型的class属性被设置为“text-box single-line”,意味着这是一个基于单行的文本框。

   1: Dummy text ... 
   2: Dummy text ... 
   3: <input class="text-box single-line" id="Foo" name="Foo" type="text" value="Dummy text ..." /> 
   4: <input class="text-box single-line" id="Bar" name="Bar" type="text" value="Dummy text ..." />

值得一提的是,ASP.NET MVC内部采用基于类型的模板匹配机制,对于字符串类型的数据成员,如果没有显式设置采用的模板名称,默认情况下会采用String模板。

Url

与EmailAddress和Html一样,模板Url也仅限于显示模式。对于某个表示为Url的字符串,如果我们希望它最终以一个连接的方式呈现在最终生成的HTML中,我们采用该模板。如下面的代码片断所示,我们通过应用UIHintAttribute特性将模板Url应用到属性Foo中。

   1: public class Model
   2: {
   3:     [UIHint("Url")]
   4:     public string Foo { get; set; }
   5: }

我们创建一个具体的Model对象,并将Foo属性设置为一个表示Url的字符串“http://www.asp.net”,最后通过如下的方式将该属性以显示模式呈现出来。

   1: @model Model
   2: @Html.DisplayFor(m=>m.Foo)

如下面的代码片断所示,该属性最终呈现为一个href属性和文本内容均属性值得连接()。

   1: <a href="http://www.asp.net">http://www.asp.neta>

MultilineText

一般的字符串在编辑模式下会呈现为一个单行的文本框(类型为“text”的元素),而MultilineText模板会将表示目标内容的字符串通过一个