模型模板

前面的Html辅助器,如Html.CheckBoxFor和Html.TextBoxFor等,是明确指定了要使用的html元素。mvc框架支持另一种方法,叫做模板视图辅助器(Templated View Helper),在这样的辅助器中,指定想要显示或编辑的模型对象或属性,而让mvc框架去判断应该用什么样的html元素。

一、使用模板视图辅助器

1、为指定的模型属性生成html

使用模板视图辅助器,意味着我们不必考虑要指定用什么样的html元素来表现一个模型属性,而是只要说出想显示哪个属性,让mvc框架自己去判断采用什么html元素来表现它。

新建mvc3项目MvcApp,在解决方案管理器中鼠标右击文件夹“Models”,选择Add->Class,添加一个类库文件,取名为TestModelClass.cs,在里面建立如下类:

namespace MvcApp.Models
{
    public 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; }
    }

    public class Address
    {
        public string Line1 { get; set; }
        public string Line2 { get; set; }
        public string City { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
    }

    public enum Role
    {
        Admin,
        User,
        Guest
    }
}

再新建HomeController控制器,完成Index动作方法:

public ActionResult Index()
{
    Person p1 = new Person { FirstName = "Joe", LastName = "Smith", IsApproved = true };
    return View(p1);
 }

在Index动作方法上添加默认试图Index.cshtml:

@model MvcApp.Models.Person

@{
    ViewBag.Title = "Index";
}

<h2>Person</h2>
<div class="field">
<label>Name:</label>
@Html.EditorFor(x=>x.FirstName)
@Html.EditorFor(x=>x.LastName)
</div>
<div class="field">
<label>Approved:</label>
@Html.EditorFor(x=>x.IsApproved)
</div>

这里使用了Html.EditorFor辅助器,它生成一个对属性进行编辑的html元素。显示结果为:

生成的html代码为:

<!DOCTYPE html>
<html>
<head>
    <title>Index</title>
    <link href="/Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
</head>
<body>
<h2>Person</h2>
<div class="field">
<label>Name:</label>
<input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="Joe" />
<input class="text-box single-line" id="LastName" name="LastName" type="text" value="Smith" />
</div>
<div class="field">
<label>Approved:</label>
<input checked="checked" class="check-box" id="IsApproved" name="IsApproved" type="checkbox" value="true" /><input name="IsApproved" type="hidden" value="false" />
</div>
</body>
</html>

可以看到,通过“x=>x.属性名”指定的属性名被用于id和name标签属性,并为FirstName和LastName属性创建了文本框(text),并为IsApproved属性创建了一个复选框(checkbox)。

也可以用另一种形式来生成html元素:以只读形式显示模型对象的值,不允许编辑。例如,将Index.cshtml修改为:

@model MvcApp.Models.Person

@{
    ViewBag.Title = "Index2";
}

<h2>Person</h2>
<div class="field">
<label>Name:</label>
@Html.DisplayFor(x=>x.FirstName)
@Html.DisplayFor(x => x.LastName)
</div>
<div class="field">
<label>Approved:</label>
@Html.DisplayFor(x => x.IsApproved)
</div>

执行后,显示结果为:

生成的html代码为:

<!DOCTYPE html>
<html>
<head>
    <title>Index2</title>
    <link href="/Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
</head>
<body>
<h2>Person</h2>
<div class="field">
<label>Name:</label>
Joe
Smith
</div>
<div class="field">
<label>Approved:</label>
<input checked="checked" class="check-box" disabled="disabled" type="checkbox" />
</div>
</body>
</html>

现在的html可以让用户看到信息,但是不能编辑。由于大多数mvc程序都是要么显示数据,要么编辑数据,所以模板辅助器是很方便的。

Html.Display("FirstName")  以只读方式显示指定属性的值。

Html.DisplayFor(x=>x.FirstName) 上一辅助器的强类型版本。

Html.Editor("FirstName")  编辑指定属性的值,根据该属性的类型和元数据选择合适的编辑器,如文本框、复选框等。

Html.EditorFor(x=>x.FirstName) 上一辅助器的强类型版本。

Html.Label("FirstName")  用<label>标签显示指定属性的属性名,而不是属性值。

Html.LabelFor(x=>x.FirstName)  上一辅助器的强类型版本。

Html.DisplayText("FirstName")   绕过所有模板,渲染指定模型属性的简单字符串表示。

Html.DisplayTextFor(x=>x.FirstName)  上一辅助器的强类型版本。

2、为一个模型对象的所有属性生成html

除了可以单独为指定的模型属性生成html以外,还可以为一个模型对象的所有属性生成html的辅助器。这一个过程称为支架(scaffolding)。

Html.DisplayForModel()

Html.EditorForModel()

Html.LabelForModel()

都是针对整个模型对象里的所有属性来进行渲染。

例如,在HomeController中新建一个动作方法:

        public ViewResult Scaffold()
        {
            Person p1 = new Person { PersonId = 1,
                FirstName = "Joe",
                LastName = "Smith",
                BirthDate = DateTime.Parse("2014/6/12"),
                IsApproved = true,
                Role = Role.User
            };
            return View(p1);
        }

为它新建默认视图Scaffold.cshtml:

@model MvcApp.Models.Person

@{
    ViewBag.Title = "Scaffold";
}

<h2>Person</h2>
@Html.EditorForModel()

执行后的效果为:

在使用辅助器时,Html.EditorForModel()为模型对象中的属性生成html标签和编辑元素,不需要把模型对象作为参数传递给这个辅助器,因为是强类型。自动将每个元素都进行处理。

3、设置生成html的样式

查看该页面的html源代码:

<h2>Person</h2>
<div class="editor-label"><label for="PersonId">PersonId</label></div>
<div class="editor-field"><input class="text-box single-line" id="PersonId" name="PersonId" type="text" value="1" /> </div>
<div class="editor-label"><label for="FirstName">FirstName</label></div>
<div class="editor-field"><input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="Joe" /> </div>
<div class="editor-label"><label for="LastName">LastName</label></div>
<div class="editor-field"><input class="text-box single-line" id="LastName" name="LastName" type="text" value="Smith" /> </div>
<div class="editor-label"><label for="BirthDate">BirthDate</label></div>
<div class="editor-field"><input class="text-box single-line" id="BirthDate" name="BirthDate" type="text" value="2014/6/12 0:00:00" /> </div>
<div class="editor-label"><label for="IsApproved">IsApproved</label></div>
<div class="editor-field"><input checked="checked" class="check-box" id="IsApproved" name="IsApproved" type="checkbox" value="true" /><input name="IsApproved" type="hidden" value="false" /> </div>
<div class="editor-label"><label for="Role">Role</label></div>
<div class="editor-field"><input class="text-box single-line" id="Role" name="Role" type="text" value="User" /> </div>

可以看到,每个元素显示的名字,和对应的编辑狂都是由<div>标签在控制,显示元素名字的<div>标签,用的class是"editor-label",构成编辑框的<div>用的class是"editor-field"。

打开解决方案管理器中的Content/Site.css文件,找到类.editor-label 和.editor-field,可以看到默认的样式为:

.editor-label
{
    margin: 1em 0 0 0;
}

.editor-field
{
    margin: 0.5em 0 0 0;
}

现在想让同一个元素的名字和对应的编辑框在一行上,就可以修改这两个类的样式如下:

.editor-label
{
    margin: 1em 0 0 0;
    clear:left;
    float:left;
    min-width: 100px;
    vertical-align:middle;
}

.editor-field
{
    margin: 0.5em 0 0 0;
    width:150px;
    float:left;
}

下面再执行,可以看到显示效果如下:

4、使用模型元数据

使用模板视图辅助器,尤其是使用它为一个模型对象的所有属性生成html时,有一个比较大的问题就是如何去控制这些属性,哪些需要显示,哪些不想显示出来,用什么类型显示等等。比如上一个例子,PersonId希望不要显示出来,因为正常情况下的程序几乎都不可能让用户直接去编辑Id值,第二个问题就是BirthDate显示成的是日期时间型,但是我们希望得到日期型。

这就需要采用模型元数据(Metadata)为这些辅助器提供指示,元数据是用注解属性来表示的,通过注解属性及参数值,给视图辅助器提供一系列指令。

(1)用元数据控制编辑及可见性

在Person类中,PersonId是不想让用户看到或编辑的属性。可以用HiddenInput注解属性,它会使辅助器渲染一个隐藏的input字段:

    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; }
    }

使用[HiddenInput]这个注解属性时,Html.EditorFor和Html.EditorForModel辅助器会对这个被修饰的属性渲染一个只读字段,例如:

[HiddenInput]显示了PersonId属性的值,但用户不能编辑它。为该属性生成的html如下:

<div class="editor-label"><label for="PersonId">PersonId</label></div>
<div class="editor-field">1<input id="PersonId" name="PersonId" type="hidden" value="1" /> </div>

id和name的值是PersonId,type的值是hidden,value的值是1,这是一个隐藏的input元素。当把这个编辑视图用于表单时,这个隐藏的input字段也是有用的。(模型绑定和模型验证还会用到)

如果想完全隐藏一个属性,可以把HiddenInput注解属性中的DisplayValue值设为false,如下:

    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; }
    }

仍然是生成隐藏的input字段,以便PersonId属性的值可以被包含到任何要递交的表单中去。但使用DisplayValue=false,可以将属性整个隐藏起来,用户也看不到,而不只是不能编辑的问题。

查看html代码,可以看到如下的代码:

<h2>Person</h2>
<input id="PersonId" name="PersonId" type="hidden" value="1" />
<div class="editor-label"><label for="FirstName">FirstName</label></div>
<div class="editor-field"><input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="Joe" /> </div>
<div class="editor-label"><label for="LastName">LastName</label></div>
<div class="editor-field"><input class="text-box single-line" id="LastName" name="LastName" type="text" value="Smith" /> </div>
<div class="editor-label"><label for="BirthDate">BirthDate</label></div>
<div class="editor-field"><input class="text-box single-line" id="BirthDate" name="BirthDate" type="text" value="2014/6/12 0:00:00" /> </div>
<div class="editor-label"><label for="IsApproved">IsApproved</label></div>
<div class="editor-field"><input checked="checked" class="check-box" id="IsApproved" name="IsApproved" type="checkbox" value="true" /><input name="IsApproved" type="hidden" value="false" /> </div>
<div class="editor-label"><label for="Role">Role</label></div>
<div class="editor-field"><input class="text-box single-line" id="Role" name="Role" type="text" value="User" /> </div>

另外,如果想把一个属性从生成的html中完全排除掉,而不仅仅是隐藏,那可以使用ScaffoldColumn注解属性。例如:

public class Person
    {
        [ScaffoldColumn(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; }
    }

当辅助器看到ScaffoldColumn注解属性时,会完全跳过该属性,不会创建隐藏input元素,该属性的细节就不会包含在生成的html中。生成html的外观与使用HiddenInput注解属性的情况相同,但是表单递交时就没有该属性的值被返回,这对模型绑定是有影响的。另外就是ScaffoldColumn注解属性对单属性辅助器不起作用,如果在视图中调用@Html.EditorFor(m=>m.PersonId),那么,即使有ScaffoldColumn注解属性存在,也会生成PersonId属性的编辑视图。

(2)使用用于标签的元数据

默认情况下,Label、LbaelFor、LabelForModel,以及EditorForModel辅助器以属性名作为它们生成的标签元素的内容(也就是生成html的label元素)。

例如,像下面这样渲染一个标签:

@Html.LabelFor(m=>m.BirthDate)

生成的html元素如下:

<label for="BirthDate">BirthDate</label>

当然,给属性定义的名字通常不是希望显示给用户的提示名字,为此可以使用DisplayName注解属性,例如:

[Display(Name="出生日期")]
 public DateTime BirthDate { get; set; }

当辅助器对BirthDate渲染html标签时,将Display注解属性,并用Name参数的值作为其内部文本,生成的html标签如下:

<div class="editor-label"><label for="BirthDate">出生日期</label></div>
<div class="editor-field"><input class="text-box single-line" id="BirthDate" name="BirthDate" type="text" value="2014/6/12 0:00:00" /> </div>

另外,还可以对一个类加一个名字,这也是为了避免在前端hmtl中直接写关于一个类的名字,设想一下,如果这个类的名字信息修改了,那就要对每个写了这个类名字的前端页面逐一修改。我们可以采用DisplayName,来对一个类取显示名字,然后用@Html.LabelForModel()来显示这个名字。例如:

    [DisplayName("人员信息")]
    public class Person
    {
        [HiddenInput(DisplayValue=false)]
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        [Display(Name="出生日期")]
        public DateTime BirthDate { get; set; }
        public Address HomeAddress { get; set; }
        public bool IsApproved { get; set; }
        public Role Role { get; set; }
    }

将Scaffold.cshtml修改为:

@model MvcApp.Models.Person

@{
    ViewBag.Title = "Scaffold";
}

<h2>Person</h2>
<h4>@Html.LabelForModel()</h4>
@Html.EditorForModel()

注意,这里就用了@Html.LabelForModel()来显示了用DisplayName注解属性给类取的名字,显示效果如下:

@Html.LabelForModel()生成的html代码为:

<h4><label for="">人员信息</label></h4>

(3)使用用于数据值的元数据

我们也可以用元数据为如何显示一个模型属性提供一些指示,可以用这个办法解决出生日期属性包含时间的问题。需要使用的注解属性是DataType:

    [DisplayName("人员信息")]
    public class Person
    {
        [HiddenInput(DisplayValue=false)]
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        [DataType(DataType.Date)]
        [Display(Name="出生日期")]
        public DateTime BirthDate { get; set; }
        public Address HomeAddress { get; set; }
        public bool IsApproved { get; set; }
        public Role Role { get; set; }
    }

DataType注解属性以DataType枚举中的一个值为参数,显示结果为:

DataType枚举中的一些常用值:

DateTime ——日期时间

Date ——日期

Time ——时间

Text ——显示单行文本

MultilineText ——将值渲染在一个文本区(textarea)元素中

Password ——以密码形式显示数据

Url ——将数据显示为一个url(用html的a标签)

EmailAddress ——将数据显示为一个e-mail地址(使用带有mailto的href的a标签)

注意这些值的效果依赖于它们所关联的属性类型,以及所使用的辅助器。例如,MultilineText值会让Editor辅助器创建一个html的文本区元素,但display辅助器对这个值是忽略的。同样,Url值只对display辅助器起作用,它渲染一个Html的a标签以创建一个链接。

(4)使用元数据选择显示模板

用显示模板来生成Html,使用UIHint注解属性来指定想用的模板,以渲染一个属性的html。例如:

    [DisplayName("人员信息")]
    public class Person
    {
        [HiddenInput(DisplayValue=false)]
        public int PersonId { get; set; }

        [UIHint("MultilineText")]
        public string FirstName { get; set; }
        public string LastName { get; set; }

        [DataType(DataType.Date)]
        [Display(Name="出生日期")]
        public DateTime BirthDate { get; set; }
        public Address HomeAddress { get; set; }
        public bool IsApproved { get; set; }
        public Role Role { get; set; }
    }

这里对FirstName指定了显示模板[UIHint("MultilineText")],当与编辑辅助器(如EditorFor或EditorForModel)一起使用时,它会为FirstName属性渲染一个html多行文本框,如下显示:

常用的UIHint显示模板如下:(内建的mvc框架的视图模板)

Boolean——(Editor辅助器)渲染一个bool值的复选框。如果是nullable的bool?值,则渲染一个带有True、False、Not Set选项的select元素。(Display辅助器)与编辑辅助器相同,但附加了disable标签属性,使生成的html元素为只读。

Collection——(Editor辅助器)为IEnumerable序列中的每一个元素渲染一个相应的模板,该序列中的各个项不必是同种类型。(Display辅助器)与编辑辅助器相同。

Decimal——(Editor辅助器)渲染一个单行文本框的input元素,并对数据值格式化,显示两位小数。(Display辅助器)渲染格式化成两位小数的数据值。

EmailAddress——(Editor辅助器)将值渲染在一个单行文本框的input元素中。(Display辅助器)用html的a标签生成一个链接,且href标记属性格式化成一个mailto的url。

HiddenInput——(Editor辅助器)创建隐藏的input元素。(Display辅助器)与编辑辅助器相同。

Html——(Editor辅助器)将值渲染在一个单行文本框的input元素中。(Display辅助器)用html的a标签生成一个链接。

MultilineText——(Editor辅助器)渲染一个含有改数据值的html textarea元素。(Display辅助器)生成数据值。

Object

Password——(Editor辅助器)将值渲染在一个单行文本框的input元素中,不以明文显示,可以编辑。(Display辅助器)渲染数据值,字符是非隐蔽的。

String——(Editor辅助器)将值渲染在单行文本框的input元素中。(Display辅助器)渲染数据值。

Text——(Editor辅助器)等同于String模板。(Display辅助器)等同于String模板。

Url——(Editor辅助器)将值渲染在单行文本框的input元素中。

(5)把元数据运用于伙伴类(Buddy Class)

有些情况下不能直接在实体模型类型上使用元数据,如前面的[HiddenInput]、[HiddenInput(DisplayValue=false)]、[Display(Name="出生日期")]、 [DisplayName("人员信息")]、[DataType(DataType.Date)]、[UIHint("MultilineText")]等元数据。因为有些模型类是自动生成的,就像使用EF实体框架之类的ORM工具那样。对这种自动生成的类所做的任何修改,比如运用一些注解属性等,都会在工具对类的再次更新时丢失。

如果想用EF实体框架来自动生成模型类,又想在这些类上使用元数据,那就应该确保把这些模型类定义成分部类(partial类),并创建第二个分部类以包含这些元数据。许多自动生成类的工具默认情况下都是创建分部类,包括EF实体框架也是这样。例如,前面的Person例子,定义的时候像下面这样处理:

a.分部模型类

    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; }
    }

由EF工具自动生成,或者自己建立,里面没有添加任何元数据。如果是EF自动建立,当EF对它进行更新时,也就是再次生成这个类时,在其上所做的修改都会丢失(比如加的注解属性等)。因此,我们创建第二个分部类,这让我们能够做一些保持附加信息的事情。当编译器建立应用程序时,这两个分部类将被合并起来。

b.定义元数据伙伴(第二个分部类,与Person要同名)

    [MetadataType(typeof(PersonMetadataSource))]
    public partial class Person
    {
    }

分部类(partial clss)必须同名,而且要在同一个命名空间中用partial关键字声明。用于元数据目的的关键注解属性是[MetadataType(typeof(PersonMetadataSource))],参数中的PersonMetadataSource就是伙伴类(buddy class),通过把伙伴类的类型作为参数,让我们把伙伴类与Person类联系在一起。也就意味着Person类的元数据可以在名为PersonMetadataSource的伙伴类中找到,伙伴类定义如下

c.伙伴类

    [DisplayName("人员信息")]
    class PersonMetadataSource
    {
        [HiddenInput(DisplayValue = false)]
        public int PersonId { get; set; }

        [UIHint("MultilineText")]
        public string FirstName { get; set; }

        [DataType(DataType.Date)]
        [Display(Name = "出生日期")]
        public DateTime BirthDate { get; set; }
    }

不用给全,只需要把带了元数据的属性给出就可以了。

执行后,显示效果,跟上一个例子一样。

5、使用复合类型参数

前面的例子中,在使用支架辅助器EditorForModel和DisplayForModel时,并未生成所有属性,例子中忽略了一个HomeAddress属性。发生这种情况是因为Object模板只对简单类型进行操作,这包括固有的c#类型,如int、bool、double等,还包括许多普通的框架类型,如Guid、DateTime等。

这就使得支架是非递归的,给定一个要处理的对象,支架模板视图辅助器将只生成简单属性类型的html,而会忽略本身是复合对象的任何属性。因此,如果要生成一个复合属性的html,我们必须明确地处理复合类属性。

可以用一个EditorForModel来处理我们视图模型对象的简单属性,然后用一个明确的Html.EditorFor调用来为HomeAddress属性(复合属性)生成html。而且,我们可以把各种元数据运用于Address类,就像在Person类上所做的那样。

修改Scaffold.cshtml如下:

@model MvcApp.Models.Person

@{
    ViewBag.Title = "Scaffold";
}

<h2>Person</h2>
<h4>@Html.LabelForModel()</h4>

<div class="column">
    @Html.EditorForModel()
</div>

<div class="column">
    @Html.EditorFor(m=>m.HomeAddress)
</div>

/Models/TestModelClass.cs内容如下:

namespace MvcApp.Models
{
    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; }
    }

    [MetadataType(typeof(PersonMetadataSource))]
    public partial class Person
    {
    }

    [DisplayName("人员信息")]
    class PersonMetadataSource
    {
        [HiddenInput(DisplayValue = false)]
        public int PersonId { get; set; }

        //[UIHint("MultilineText")]
        public string FirstName { get; set; }

        [DataType(DataType.Date)]
        [Display(Name = "出生日期")]
        public DateTime BirthDate { get; set; }
    }

    public class Address
    {
        public string Line1 { get; set; }
        public string Line2 { get; set; }
        public string City { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
    }

    public enum Role
    {
        Admin,
        User,
        Guest
    }
}

HomeController.cs中的动作方法Scaffold代码如下:

        public ViewResult Scaffold()
        {
            Person p1 = new Person { PersonId = 1,
                FirstName = "Joe",
                LastName = "Smith",
                BirthDate = DateTime.Parse("2014/6/12"),
                IsApproved = true,
                Role = Role.User
            };
            return View(p1);
        }

在/Content/Site.css中增加样式如下:

/*Additional Styles 新增样式
********************************************************/
.label
{
    vertical-align:middle;
}
.check-box
{
    vertical-align:bottom;
    margin: .5em 0 0 0;
}

div.column
{
    float:left;
    width:auto;
}
.single-line
{
    width: 100px;
}

执行后的显示效果为:

二、定制模板视图辅助器系统

前面演示了如何用元数据来形成模板辅助器渲染数据的方式。但对于mvc框架,还有一些高级选项,能够让我们完全定制模板辅助器。

1、创建自定义编辑模板(Editor)

以Person类中的Role属性为例,这个属性的取值,是自定义的枚举类型Role中的某一个值,先看一下使用内建的模板辅助器对这个属性进行渲染的例子。假设还是在Scaffold.cshtml中添加了如下代码:

<p>
    @Html.LabelFor(m => m.Role):
    @Html.EditorFor(m => m.Role)
</p>
<p>
    @Html.LabelFor(m=>m.Role):
    @Html.DisplayFor(m=>m.Role)
</p>

这一部分生成的显示效果为:

Label和Display的模板都可以符合要求,但是Editor模板生成的效果不太符合我们的要求。枚举类型Role中只定义了三个值(Admin、User、Guest),现在生成的编辑框允许用户给这个属性输入任意值。对于这样的情况我们就可以采用自定义编辑模板。步骤如下:

在/Views/Shared文件夹中新建一个名为EditroTemplates的文件夹,然后右击该文件夹,选择Add->View,为视图取名为Role,并选中Create as a partial view。再选中Create a strongly-typed view创建强类型视图,把Model class设置为Role。创建了这个分部视图后,就可以添加标准的Razor语法,以生成所需要的编辑视图。创建其html有很多方法,最简单的是混合使用静态html元素和Razor标签

@model MvcApp.Models.Role
@using MvcApp.Models

<select id="Role" name="Role">
    @foreach (Role value in Enum.GetValues(typeof(Role)))
    {
        <option value="@value" @(Model == value ? "selected=\"selected\"" : "")>
            @value
        </option>
    }
</select>

这个分部视图创建了一个html的select元素,并用Role枚举中的每个值填充它的option元素。在foreach循环中检查所传递的值是否与当前元素匹配,以便能正确设置selected属性。

当要生成这个属性的一个Editor视图时,这个分部视图就被用来生成html。例如Scaffold.cshtml

@model MvcApp.Models.Person

@{
    ViewBag.Title = "Scaffold";
}

<p>
    @Html.LabelFor(m => m.Role):
    @Html.EditorFor(m => m.Role)
</p>
<p>
    @Html.LabelFor(m=>m.Role):
    @Html.DisplayFor(m=>m.Role)
</p>

<h2>Person</h2>
<h4>@Html.LabelForModel()</h4>

<div class="column">
    @Html.EditorForModel()
</div>

<div class="column">
    @Html.EditorFor(m=>m.HomeAddress)
</div>

生成的效果为:

可以看到无论是使用的单个属性辅助器还是支架辅助器,它们都会查找并使用这个Role.cshtml模板。注意,模板那的名称(指Role.cshtml中的Role)对应于属性的类型而不是属性名,因此凡是使用了Role类型的属性都会运用这个自定义模板。

在这个例子中,包括文件夹的名字,以及文件名,都是要遵循的约定,不能随便乱取。Views/Shared/EditorTemplates文件夹,放在里面的Role.cshtml都是遵循约定。

Role.cshtml就是自定义模板(Template)。Role.cshtml模板之所以能够工作,是因为mvc框架在使用内建模板之前,会对一个给定的c#类型查找自定义模板。查找的顺序如下:

(1)传递给辅助器的模板——例如,Html.EditorFor(m=>m.SomeProperty, “MyTemplate”)将导致使用MyTemplate模板。

(2)由元数据注解属性指定的任意模板,如UIHint。

(3)与元数据指定的任意数据类型相关联的模板,如DataType注解属性。

(4)与待处理数据类型的.NET类名对应的任意模板。(与类型同名的模板)

(5)如果被处理的数据类型是一个简单类型,那么便采用内建的String模板。

(6)对应于数据类型基类的任意模板。

(7)如果数据类型实现IEnumerable,那么将使用内建的Collection模板。

(8)如果上述全部失败,将使用Object模板——服从于支架非递归规则。

在模板搜索的每一个阶段,mvc框架都会查找一个名为EditorTemplates/<name>DisplayTemplates/<name>的模板。对于本例子中的Role.cshtml模板,满足上面顺序中的第4步,因为我们创建了一个名为Role.cshtml的模板,并把它放在了/Views/Shared/EditorTemplates文件夹中。

自定义模板是用搜索常规视图的同样方式来查找的,这意味着我们可以创建一个控制器专用的自定义模板,并把它放在/Views/<controller>/EditorTemplates文件夹中,以覆盖在/Views/Shared文件夹中找到的模板。

2、创建自定义显示模板(display)

过程类似于创建自定义编辑模板,只不过自定义显示模板被放在DisplayTemplates文件夹中。下面的例子演示了一个Role枚举的自定义显示模板,把这个模板文件创建为/Views/Shared/DisplayTemplates/Role.cshtml

@model MvcApp.Models.Role
@using MvcApp.Models

@foreach (Role value in Enum.GetValues(typeof(Role)))
{
    if (value == Model)
    {
        <b>@value</b>
    }
    else
    {
        @value
    }
}

这个模板列举枚举Role中的所有值,如果与传递给当前视图的数据值相等,那就用加粗显示。

例如,如果在Scaffold.cshtml中有使用Display辅助器:

<p>
    @Html.LabelFor(m => m.Role):
    @Html.EditorFor(m => m.Role)
</p>
<p>
    @Html.LabelFor(m=>m.Role):
    @Html.DisplayFor(m=>m.Role)
</p>

那么执行后的显示结果为:

可以看到在Display辅助器对应的地方,当前数据被加粗了。

3、创建泛型模板

不光是可以创建类型专用的模板,例如,还可以创建一个工作与所有枚举的模板,并且随后指定用UIHint注解属性来选择这个模板。根据前面的模板搜索顺序,可以看到用UIHint注解属性指定的模板优先于类型专用模板。

下面在/Views/Shared/EditorTemplates文件夹中新建Enum.cshtml模板,这个模板是对c#枚举的一个更通用的处理。

@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
        };
    }
    ))

该模板的视图模型是Enum,它可以枚举任何类型。可以再次混用静态html和Razor语法来创建这个模板,但在这个例子中使用了强类型的DropDownListFor辅助器,并使用了一些LINQ来把枚举值转换成SelectListItems。

使用的时候,就用:

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

4、替换内建模板

如果创建一个与内建模板同名的自定义模板,mvc框架将优先于内建模板来使用这个自定义版本。

比如,对于bool类型的IsApproved属性

    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; }
    }

    [MetadataType(typeof(PersonMetadataSource))]
    public partial class Person
    {
    }

    [DisplayName("人员信息")]
    class PersonMetadataSource
    {
        [HiddenInput(DisplayValue = false)]
        public int PersonId { get; set; }

        //[UIHint("MultilineText")]
        public string FirstName { get; set; }

        [DataType(DataType.Date)]
        [Display(Name = "出生日期")]
        public DateTime BirthDate { get; set; }

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

HomeController里的Scaffold动作方法为:

        public ViewResult Scaffold()
        {
            Person p1 = new Person { PersonId = 1,
                FirstName = "Joe",
                LastName = "Smith",
                BirthDate = DateTime.Parse("2014/6/12"),
                IsApproved = true,
                Role = Role.User
            };
            return View(p1);
        }

如果使用了Display辅助器,来显示bool类型的IsApproved属性

<p>
    @Html.LabelFor(m=>m.IsApproved):
    @Html.DisplayFor(m=>m.IsApproved)
</p>

显示效果为:

因为用的是Display辅助器,所以不能编辑,以灰色显示。

现在自定义内建模板Boolean,要在Display辅助器中起效果,把它放在/Views/Shared/DisplayTemplates/Boolean.cshtml中。

@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
}

显示结果为:

5、使用ViewData.TemplateInfo属性

mvc提供ViewData.TemplateInfo属性,使得编写自定义视图模板更容易。该属性返回一个TemplateInfo对象。

这个类的一些常用成员:

FormattedModelValue——返回当前模型的字符串表示,所返回的字符串考虑了格式化元数据(如DataType注解属性等)。

GetFullHtmlFieldId()——返回可以用于html的id标签属性的字符串。

GetFullHtmlFieldName()——返回可以用于html的name标签属性的字符串。

HtmlFieldPrefix——返回字段前缀

(1)关注数据格式化

下面举一个例子,使Model中能使用这个placeholder,在编辑框上给出提示信息。

首先,我们要新建一个PlaceHolderAttribute类,让它继承Attribute, IMetadataAware两个接口。在解决方案管理器中新建一个文件夹Infrastructures,在文件夹里新建类库文件PlaceHolderAttribute.cs

namespace MvcApp.Infrastructures
{
    public class PlaceHolderAttribute : Attribute, IMetadataAware
    {
        private readonly string _placeholder;
        public PlaceHolderAttribute(string placeholder)
        {
            _placeholder = placeholder;
        }

        public void OnMetadataCreated(ModelMetadata metadata)
        {
            metadata.AdditionalValues["placeholder"] = _placeholder;
        }
    }
}

再自定义一个PlaceHolder的显示模板,一般用文本框接收的数据通常都是string类型的,所以针对string类型,编写类型的自定义模板,所以以类型名string为文件名,创建类型的自定义模板,位置在/Views/Shared/EditorTemplates/string.cshtml

@{
    var placeholder = string.Empty;
    if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("placeholder"))
    {
        placeholder = ViewData.ModelMetadata.AdditionalValues["placeholder"] as string;
    }
}
@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { placeholder = placeholder })

接下来就可以在模型类上使用我们自定义的PlaceHolder类(当然,全称是PlaceHolderAttribute累,使用时只用Attribute之前的名字)了,例如:

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; }
    }

    [MetadataType(typeof(PersonMetadataSource))]
    public partial class Person
    {
    }

    [DisplayName("人员信息")]
    class PersonMetadataSource
    {
        [HiddenInput(DisplayValue = false)]
        public int PersonId { get; set; }

        //[UIHint("MultilineText")]
        [PlaceHolder("firstname")]
        public string FirstName { get; set; }

        [DataType(DataType.Date)]
        [Display(Name = "出生日期")]
        public DateTime BirthDate { get; set; }

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

这个例子中,只对FirstName使用了我们自定义的PlaceHolder类

[PlaceHolder("firstname")]
      public string FirstName { get; set; }

当用 @Html.EditorFor(m=>m.FirstName)来生成FirstName属性时,如果文本框中没有内容,或者有内容时把内容删除掉,那么PlaceHolder的提示信息就会显示出来,以灰色显示。

在这个例子中,我们定义了string.cshtml后,导致css格式发生了错位,所以右边第二列有错位重叠的现象。

(2)使用html前缀

当渲染一个有层次结构的视图时,mvc框架会跟踪正在渲染的属性名,并通过TemplateInfo对象的HtmlFieldPrefix属性,为我们提供一个唯一的参考点。例如Person类的HomeAddress属性,这是一个Address对象。如果像下面这样渲染一个属性的模板:

@Html.EditorFor(m=>m.HomeAddress.PostalCode)

那么,传递给这个模板的HtmlFieldPrefix的值将是HomeAddress.PostalCode。HtmlFieldPrefix属性所渲染的值通常不能直接作为html标签属性的值,因此TemplateInfo对象包含了GetFullHtmlFieldId方法和GetFullHtmlFieldName方法,以便把这个唯一的标识转换成可用的东西。

lyj

模型模板

时间: 2024-10-11 16:05:17

模型模板的相关文章

ASP.NET MVC —— Model之一模型模板

http://www.cnblogs.com/lzhp/archive/2013/03/25/2981650.html Mvc model系列文章主要分为三部分:Model Templates,Model Binding,Model Validation.本篇文章主要内容包括下面三个部分: A.使用模板视图助手 B.自定义视图模板系统 C.理解元数据提供体系 一.使用模板视图助手 1.1助手体验 模板视图助手,我理解为MVC提供的根据model中定义的数据类型,来生成视图(View)标签的助手.

ASP.NET MVC学习之模型模板篇

一.前言 如果你使用ASP.NET MVC制作后台一定会爱上它的EditorForModal.DisplayForModal和LabelForModal方法,因为这些方法可以将模型直接变成对应的标签,省了不少事,但是对于一些苛刻的人来说,一定想自定义,下面我们会先介绍如何使用,然后介绍如何自定义. 二.正文 1.输出模型 首先我们要新建一个Home控制器,对应的还要有一个Index动作,和Index视图,接着我们在Modal下新建一个Address类: 1 namespace MvcStudy.

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

和普通HTML帮助函数不同,模板帮助函数不需要指定所用的HTML类型,MVC会推断选择合适的HTML元素,这让我们有更多的灵活性. 使用模板帮助函数 我们使用<ASP.NET MVC 4 (六) 帮助函数>中的数据模型和控制器继续后面的例子,使用模板帮助函数后改写编辑输入的视图: @model HelperMethods.Models.Person @{ ViewBag.Title = "CreatePerson"; } <h2>CreatePerson<

自定义模型使用教程

在织梦系统中有内容模型这个概念,不同内容模型可以用来构建不同内容形式的站点,在系统中自带了以下几种模型:普通文章.图集.软件.商品.分类信息.专题.通过系统自带的模型,我们可以用来构建不同类型的站点,例如:使用图集可以做一个图片站,用软件模型构建一个软件下载站点.当然以上随系统附带的模型被称为系统模型,用户可以自己定义一些模型,比如图书.音乐专辑等,自定义了这些模型才可以构建更多内容形式的站点,本篇将讲述如何使用系统的自定义模型管理功能来实现内容模型的构建.我们首先登录系统后台,点击[核心]-[

NET MVC RazorEngine 解析模板生成静态页

ASP.NET MVC 解析模板生成静态页一(RazorEngine) 简述 Razor是ASP.NET MVC 3中新加入的技术,以作为ASPX引擎的一个新的替代项.在早期的MVC版本中默认使用的是ASPX模板引擎,Razor在语法上的确不错,用起来非常方便,简洁的语法与.NET Framework 结合,广泛应用于ASP.NET MVC 项目. 我们在很多项目开发中会常常用到页面静态化,页面静态化有许多方式,最常见的就是类似很多PHP CMS种使用的 标签替换的方式(如:帝国CMS.EcSh

Razor - 模板引擎 / 代码生成 - RazorEngine

目录 Brief Authors Official Website RazorEngine 的原理 - 官方解释 安装记录 Supported Syntax (默认实现支持的语法) 测试记录 - can't cleanup temp files 测试记录 - Quick Start 测试记录 - Configuration 测试记录 - 对比 3 种 Type 的 model 的语法 测试记录 - 扩展模板语法 测试记录 - Layout 测试记录 - Partial (@Include())

C# 取Visio模型信息的简易方法

最近的一个项目,要求导出Visio图纸,因为是建筑类的,所以,需要设置墙壁,门,房间等信息的参数. 拿墙壁为例,选中墙壁模型,右键属性,会弹出以下对话框. 需要设置墙长.墙壁厚度等一些列信息. 现在C#操作Visio里例子比较少,所以,花了好久,都没有看到有用的帖子,直到今天下午,在Bing里发现了一个帖子,顿时让我受益匪浅.原贴地址:http://mikeborozdin.com/post/reading-and-writing-visio-shape-information-with-c/

MVC模型与MTV模型

MVC模型: MVC(Model View Controller 模型-视图-控制器)是一种Web架构的模式,它把业务逻辑.模型数据.用户界面分离开来,让开发者将数据与表现解耦,前端工程师可以只改页面效果部分而不用接触后端代码,DBA可以重新命名数据表并且只需更改一个地方,无需从一大堆文件中进行查找和替换. MVC将web应用分为三层,分别是模型,视图,控制器.松耦合的连接方式,类似于插件的感觉.可以提高代码复用的能力. MVC特点是单向性: 1. 浏览器发送请求 2. 控制器和模型交互获取数据

Java之利用Freemarker模板引擎实现代码生成器,提高效率

http://blog.csdn.net/huangwenyi1010/article/details/71249258 目录(?)[-] 开心一笑 视频教程 提出问题 解决问题 前言 技术选型 实现思路 首先假如在数据库中有一张表 ay_test 我们首先要获取数据库的连接这里我只贴出相关的代码 获取数据库表的元数据 最后根据元数据获取表字段注释等等生成相关的文件 代码实现 Java代码实现 FreeMarkerTemplateUtils工具类 实体类 ColumnClass freemark