ASP.NET MVC 4 (七) 模板帮助函数

和普通HTML帮助函数不同,模板帮助函数不需要指定所用的HTML类型,MVC会推断选择合适的HTML元素,这让我们有更多的灵活性。

使用模板帮助函数

我们使用《ASP.NET MVC 4 (六) 帮助函数》中的数据模型和控制器继续后面的例子,使用模板帮助函数后改写编辑输入的视图:

@model HelperMethods.Models.Person
@{
    ViewBag.Title = "CreatePerson";
}
<h2>CreatePerson</h2>
@using (Html.BeginRouteForm("FormRoute", new { }, FormMethod.Post,new { @class = "personClass", data_formType = "person" }))
{
    <div class="dataElem">
        <label>PersonId</label>
        @Html.Editor("PersonId")
    </div>
    <div class="dataElem">
        <label>First Name</label>
        @Html.Editor("FirstName")
    </div>
    <div class="dataElem">
        <label>Last Name</label>
        @Html.EditorFor(m => m.LastName)
    </div>
    <div class="dataElem">
        <label>Role</label>
        @Html.EditorFor(m => m.Role)
    </div>
    <div class="dataElem">
        <label>Birth Date</label>
        @Html.EditorFor(m => m.BirthDate)
    </div>
    <input type="submit" value="Submit" />
}

这里用到模板帮助函数Editor和EditorFor,MVC猜测相应的数据类型生成相应类型的输入HTML标记:

...
<h2>CreatePerson</h2>
    <form action="/app/forms/Home/CreatePerson" class="personClass"
          data-formtype="person" method="post">
        <div class="dataElem">
            <label>PersonId</label>
            <input class="text-box single-line" id="PersonId" name="PersonId" type="number" value="0" />
        </div>
        <div class="dataElem">
            <label>First Name</label>
            <input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="" />
        </div>
        <div class="dataElem">
            <label>Last Name</label>
            <input class="text-box single-line" id="LastName" name="LastName" type="text" value="" />
        </div>
        <div class="dataElem">
            <label>Role</label>
            <input class="text-box single-line" id="Role" name="Role" type="text" value="Admin" />
        </div>
        <div class="dataElem">
            <label>Birth Date</label>
            <input class="text-box single-line" id="BirthDate" name="BirthDate" type="datetime" value="01/01/0001 00:00:00" />
        </div>
        <input type="submit" value="Submit" />
    </form>
...

HTML5规范定义了input标签可以编辑通用类型,比如数字、日期,不同浏览器对HTML5的支持有所差别,上面的结果在Opera中number类型会渲染成带spin按钮的编辑空间,datetime为渲染成专用的带可选日历的日期/时间编辑器。

以下是MVC可用的模板帮助函数列表:

帮助函数 示例 说明
Display Html.Display("FirstName") 渲染只读的HTML元素,根据数据类型和metadata选择适用的HTML元素
DisplayFor Html.DisplayFor(x => x.FirstName) Display的强类型形式
Editor Html.Editor("FirstName") 渲染可编辑的HTML元素,根据数据类型和metadata选择适用的HTML元素
EditorFor Html.EditorFor(x => x.FirstName) Editor的强类型形式
Label Html.Label("FirstName") 根据引用的模型对象属性渲染<label>标签
LabelFor Html.LabelFor(x => x.FirstName) Label的强类型形式

这个例子为我们演示如何使用Display和Label:

@model HelperMethods.Models.Person
@{
    ViewBag.Title = "DisplayPerson";
}
<h2>DisplayPerson</h2>
<div class="dataElem">
    @Html.Label("PersonId")
    @Html.Display("PersonId")
</div>
<div class="dataElem">
    @Html.Label("FirstName")
    @Html.Display("FirstName")
</div>
<div class="dataElem">
    @Html.LabelFor(m => m.LastName)
    @Html.DisplayFor(m => m.LastName)
</div>
<div class="dataElem">
    @Html.LabelFor(m => m.Role)
    @Html.DisplayFor(m => m.Role)
</div>
<div class="dataElem">
    @Html.LabelFor(m => m.BirthDate)
    @Html.DisplayFor(m => m.BirthDate)
</div> 

输出的HTML结果:

...
<div class="dataElem">
<label for="PersonId">PersonId</label>
100
</div>
<div class="dataElem">
<label for="FirstName">FirstName</label>
Adam
</div>
<div class="dataElem">
<label for="LastName">LastName</label>
Freeman
</div>
<div class="dataElem">
<label for="Role">Role</label>
Admin
</div>
<div class="dataElem">
<label for="BirthDate">BirthDate</label>
01/01/0001 00:00:00
</div>
...

整模型模板帮助函数

上面的模板帮助函数可以处理单个模型对象属性,MVC还提供一组模板帮助函数为整个模型对象生成HTML:

帮助函数 示例 说明
DisplayForModel Html.DisplayForModel() 为整个模型对象生成只读的HTML渲染
EditorForModel Html.EditorForModel() 为整个模型对象生成可编辑的HTML渲染
LabelForModel Html.LabelForModel()  为整个模型对象生成<label>标签

使用整模型帮助函数后编辑Person的视图可以简化为:

@model HelperMethods.Models.Person
@{
  ViewBag.Title = "CreatePerson";
}
<h2>CreatePerson: @Html.LabelForModel()</h2>
@using(Html.BeginRouteForm("FormRoute", new {}, FormMethod.Post, new { @class = "personClass", data_formType="person"})) {
  @Html.EditorForModel()
  <input type="submit" value="Submit" />
} 

MVC会为Person的各个属性生成相应的HTML编辑元素,枚举类型也被渲染为一个简单的编辑框,这也并不是很有用,我们更习惯从下拉框中选择枚举值(后面我们可以看到如何实现)。另外并非所有的属性,比如Address属性,它不是一个c#的元类型,在生成的结果中不可见,我们可以为Address在调用一次EditFor来展开显示它(后面我们可以看到的object模板):

@model HelperMethods.Models.Person
@{
    ViewBag.Title = "CreatePerson";
}
<h2>CreatePerson: @Html.LabelForModel()</h2>
@using (Html.BeginRouteForm("FormRoute", new { }, FormMethod.Post,new { @class = "personClass", data_formType = "person" }))
{
    <div class="column">
        @Html.EditorForModel()
    </div>
    <div class="column">
        @Html.EditorFor(m => m.HomeAddress)
    </div>
    <input type="submit" value="Submit" />
} 

这里使用的是强类型的EditFor,以保证生成HTML元素包含正确的ID和Name,比如HomeAddress.Line1生成id="HomeAddress_Line1",name="HomeAddress.Line1",这能保证数据提交后正确绑定到数据模型上。

使用模型metadata

EditorForModel()生成的HTML并不那么完美,这不能怪罪于模板帮助函数,它已经对要显示的结果做了最好的猜测,我们可以通过metadata给予模板帮助函数更多的提示。比如PersonId我们是不能编辑的,我们可以使用HiddenAttribute标记:

public class Person {
[HiddenInput]
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public Address HomeAddress { get; set; }
public bool IsApproved { get; set; }
public Role Role { get; set; }
} 

EditorForModel会渲染一个只读的input元素:

...
<div class="editor-field">
0
<input id="PersonId" name="PersonId" type="hidden" value="0" />
</div>
... 

如果我们要完全不显示PersonId:

public class Person {
[HiddenInput(DisplayValue=false)]
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public Address HomeAddress { get; set; }
public bool IsApproved { get; set; }
public Role Role { get; set; }
} 

Html.EditorForModel()会生成一个隐藏的input元素,我们看不到它,但是仍然包含在提交的数据中。如果我们要完完全全的忽略一个属性,连隐藏input元素都不要生成:

...
[ScaffoldColumn(false)]
public int PersonId { get; set; }
... 

需要注意的是ScaffoldColumn(false)只对EditorForModel()有作用,单个属性的模板帮助函数比如 @Html.EditorFor(m => m.PersonId)仍然会生成结果,不受ScaffoldColumn影响。

我们还可以通过metadata的方式设定label帮助函数生成的标签内容:

[DisplayName("New Person")]
public class Person {
[HiddenInput(DisplayValue=false)]
public int PersonId { get; set; }
[Display(Name="First")]
public string FirstName { get; set; }
[Display(Name = "Last")]
public string LastName { get; set; }
[Display(Name = "Birth Date")]
public DateTime BirthDate { get; set; }
public Address HomeAddress { get; set; }
[Display(Name="Approved")]
public bool IsApproved { get; set; }
public Role Role { get; set; }
} 

Label帮助函数会使用DisplayName定义的名称“New person”为整个model生成Label标签,使用Display中的Name为各个属性生成Label标签。

我们可以使用DataType特性指定属性的数据类型:

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

通过指定BirthDate的数据类型为Date,生成的HTML编辑框会只包含日期部分。可用的数据类型包括:DateTime、Date、Time、Text、PhoneNumber、MultilineText、Password、Url、EmailAddress。模板帮助函数根据数据类型的不同选择生成不同的标签元素,比如MultilineText生成textarea多行编辑框。

除了通过DateType指定属性的数据类型,我们还可以使用 UIHint特性明确指定帮助函数生成HTML选用的标签元素:

...
[Display(Name="First")]
[UIHint("MultilineText")]
public string FirstName { get; set; }
...

这里明确指定为FirstName使用一个textarea编辑框,EditorFor和EditorForModel参考这个特性。可以指定的UI模板包括:

UIHint模板 Editor输出结果 Display输出结果
Boolean bool值生成一个复选框,bool?生成包含true、false、not set三个选项的选择框 和editor相同但是带有disabled属性禁止编辑
Collection 为IEnumerable中每个项目选择合适的目标生成结果,项目不一定是相同的类型 等同Editor
Decimal 生成单行textbox类型的input元素,格式化带2个小数点的 显示带2个小数点的字符串
DateTime 生成type=datetime的input元素,包含日期和时间 显示日期和时间
Date 生成type=date的input元素,仅包含日期 显示日期
EmailAddress 生成单行textbox的input元素 生成a元素,href=mailto:
HiddenInput 生成隐藏的input元素 生成隐藏Input
Html 生成单行textbox的input元素 生成a元素标记
MultilineText 生成textarea元素 显示数据 
Number 生成type=number的input元素  显示数据 
Object 展开对象,为对象的各个属性生成合适的元素,展开不能递归,也就是说如果某个属性不是一个基本类型就再展开 
Password 生成密码类型的单行textbox 显示模糊处理后的数据
String 生成单行textbox的input元素 显示数据
Text 同string 同string
Tel 生成type=tel的input元素 显示数据
Time 生成type=time的input元素,仅显示时间 显示时间数据
Url 生成单行input元素 生成a元素,内部HTML和href属性都设置为数值

需要注意的是如果所选UI模板和数据类型冲突会产生异常,比如为string数据类型选择boolean的UI模板。

我们不需要直接在模型类上编辑metadata属性,特别是那些ORM自动生成的模型类,每次修改数据Schema时就会重建模型类,模型类上的metadata被清除,我们不得不重新编辑metadata,针对这种情况我们可以定义模型类为partial,把metadata放到单独的伙伴类中:

[MetadataType(typeof(PersonMetaData))]
    public partial class Person {
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
        public Address HomeAddress { get; set; }
        public bool IsApproved { get; set; }
        public Role Role { get; set; }
    }

[DisplayName("New Person")]
    public partial class PersonMetaData {
        [HiddenInput(DisplayValue=false)]
        public int PersonId { get; set; }

        [Display(Name="First")]
        public string FirstName { get; set; }

        [Display(Name = "Last")]
        public string LastName { get; set; }

        [Display(Name = "Birth Date")]
        public DateTime BirthDate { get; set; }

        [Display(Name="Approved")]
        [UIHint("Boolean")]
        public bool IsApproved { get; set; }

        [UIHint("Boolean")]
        public Role Role { get; set; }
    }

伙伴类中不需要包含每个属性,我们可以只为需要的属性设置metadata。

自定义编辑模板

我们可以通过创建自定义模板进一步控制模板帮助函数生成的HTML结果,MVC在 /Views/Shared/EditorTemplates目录下查找自定义的模板,我们可以创建对应某个数据类型的强分部视图,比如Role枚举类型,我们为它创建Role.cshtml:

@model HelperMethods.Models.Role
@Html.DropDownListFor(m => m, new SelectList(Enum.GetNames(Model.GetType()), Model.ToString())) 

这里为Role类型创建了一个下拉选择对话框,MVC会在使用内建模板前搜索到这个自定义的模板并使用它。MVC按照一定的顺序搜索使用适合的模板:

  1. 帮助函数中指定的模板,比如 Html.EditorFor(m => m.SomeProperty, "MyTemplate")指定的MyTemplate模板
  2. metadata中UIHint指定的目标
  3. 数据类型确定的模板,比如DataType特性
  4. 正在处理的数据类型类的名称
  5. 对于简单类型使用内建的string模板
  6. 如果数据类型实现IEnumerable,使用内建的Collection模板
  7. 以上失败时,使用ojbect模板展开,展开不能递归,也就是不展开子类型的属性

根据上面的模板搜索顺序,可以将Role模板变得更广泛化,我们创建一个Enum类型都适用的模板:

@model Enum

@Html.DropDownListFor(m => m, Enum.GetValues(Model.GetType())
    .Cast<Enum>()
    .Select(m => {
        string enumVal = Enum.GetName(Model.GetType(), m);
        return new SelectListItem() {
            Selected = (Model.ToString() == enumVal),
            Text = enumVal,
            Value = enumVal
        };
    }))

我们在metadata伙伴类中指定Role属性使用这个Enum模板:

[DisplayName("New Person")]
    public partial class PersonMetaData1 {
        ...
        [UIHint("Enum")]
        public Role Role { get; set; }
    }

而如果我们创建了一个和内建同名的模板会怎么样?MVC会使用我们自定义的模板代替内建的模板,比如我们为bool和bool?创建一个替代Boolean内建类型的模板:

@model bool?

@if (ViewData.ModelMetadata.IsNullableValueType && Model == null) {
    @:(True) (False) <b>(Not Set)</b>
} else if (Model.Value) {
    @:<b>(True)</b> (False) (Not Set)
} else {
    @:(True) <b>(False)</b> (Not Set)
}

以上为对《Apress Pro ASP.NET MVC 4》第四版相关内容的总结,不详之处参见原版 http://www.apress.com/9781430242369

时间: 2024-10-15 11:47:06

ASP.NET MVC 4 (七) 模板帮助函数的相关文章

七天学会ASP.NET MVC(七)——创建单页应用

注:本文为学习摘录,原文地址为:http://www.cnblogs.com/powertoolsteam/p/MVC_Seven.html 目录 引言 最后一篇学什么 实验32—整理项目组织结构 关于实验32 实验33——创建单页应用——第一部分—安装 什么是Areas? 关于实验33 实验34——创建单页应用——第二部分—显示Employee 实验35——创建单页应用——第三部分—新建Employee 实验36——创建单页应用——第三部分—上传 实验32 ———整理项目组织结构 实验32与其

[转]ASP.net MVC 2 自定义模板来显示数据

本文转自:http://blog.163.com/[email protected]/blog/static/1394892972012113104653651/ 在ASP.net MVC 2中,一个很有意思,也很实用的功能,就是可以模板化地显示数据模型.什么叫模板化地显示数据模型呢?MVC可以指定某种数据类型在视图中呈现时,以何种形式来进行呈现.一般情况下,MVC框架会将模型中和各个字段以字符串的形式进行渲染,所以,我们看到的就是字符串.然而,我们有了模板化帮助器,我们可以更加自由地来装饰我们

ASP.NET MVC 5 默认模板的JS和CSS 是怎么加载的?

当创建一个默认的mvc模板后,项目如下: 运行项目后,鼠标右键查看源码,在源码里看到头部和尾部都有js和css文件被引用,他们是怎么被添加进来的呢? 首先我们先看对应的view文件index.cshtml,发现并没有任何加载js和css文件的标识. 这个时候我们就想到了,会不会是布局模板里加载了呢,因为index.cshtml页面是引用了模板的,这里的模板引用机制是,通过_ViewStart.cshtml文件来设置的.当index.cshtml没有任何设置模板页的时候,就默认把_ViewStar

ASP.NET MVC 系列:模板化机制

模版化的核心是定制ViewEngine.完整的模板化必须实现3个功能:1.网站的多套模版(razor)支持 2.模版的多样式(style)支持. 3.按需实现模版:没有实现的加载默认模版. 我们除了要达到上述3个功能目标,还必须不能影响MVC默认的项目目录和文件布局以及开放工具的智能提示,这样才是真正可用的.依惯例,喜欢自己研究的直接看Nop的源代码,喜欢直接看核心代码和演示Demo继续. 基础准备 1.新建一个空白ASP.NET应用程序,添加MVC引用.添加必要的Content.Scripts

MVC系列学习(七)-模板页

1.新建一个MVC项目,选择基本 2.查看文件 看到VS为我们生成了一些东西 布局页面,Layout 指定了模板页 3.开始实例 首先控制器中的代码如下: 视图中代码如下: 1.在/Views/_ViewStart.cshtml[如果/Views下有中有该 子视图,运行时都先加载它先则它的优先级高与其他视图] 2.在/Views/Shared/_Layout.cshtml[模板页或布局页,如果视图中没设置Layout=null的话,都默认调用布局页] 3.在/Views/Home/_ViewSt

【MVC】ASP.NET MVC 4项目模板的结构简介

引言     在VS2012新建一个窗体验证的MVC 4项目后,可以看到微软已经帮我们做了很多了,项目里面该有的都有了,完全可以看成一个简单网站.作为开发,能理解里面文件结构和作用,也算是半只脚踏进MVC的大门了.下面依次介绍其中的文件作用. App_Start AuthConfig.cs AuthConfig类的RegisterAuth用来注册外部登录,具体作用在于用户可以用外部的账号登录本网站. BundleConfig.cs BundleConfig类的RegisterBundles可以将

ASP.Net MVC 布局页 模板页 使用方法详细说明

一.Views文件夹 -> Shared文件夹下的 _Layout.cshtml 母版页 @RenderBody 当创建基于_Layout.cshtml布局页面的视图时,视图的内容会和布局页面合并,而新创建视图的内容会通过_Layout.cshtml布局页面的@RenderBody()方法呈现在标签之间. @RenderPage从名称可以猜出来这个方法是要呈现一个页面.比如网页中固定的头部可以单独放在一个共享的视图文件中,然后在布局页面中通过这个方法调用,用法如下:@RenderPage(“~/

ASP.NET MVC中切换模板页(不同目录的cshtml文件)

不想打字,看图,看来以后建立一个父类控制器还是有必要的...

七天学会ASP.NET MVC (四)——用户授权认证问题

小编应各位的要求,快马加鞭,马不停蹄的最终:七天学会 Asp.Net MVC 第四篇出炉.在第四天的学习中.我们主要了学习怎样在MVC中怎样实现认证授权等问题.本节主要讲了验证错误时的错误值,client验证,授权认证及登录注销功能的实现. 系列文章 七天学会ASP.NET MVC (一)--深入理解ASP.NET MVC 七天学会ASP.NET MVC (二)--ASP.NET MVC 数据传递 七天学会ASP.NET MVC (三)--ASP.Net MVC 数据处理 七天学会ASP.NET