ASP.NET MVC 2 Templates, Part 4: Custom Object Templates[翻译]

原文链接

ASP.NET MVC 2 Templates, Part 1: Introduction[翻译]

ASP.NET MVC 2 Templates, Part 2: ModelMetadata[翻译]

ASP.NET MVC 2 Templates, Part 3: Default Templates[翻译]

自定义模版

在Part 3,我们看到把内置模版写成.ascx文件是什么样子的.在这篇文章,我们会讨论Object模版的一些自定义方法,使得其有不同的特性和不同的显示模版UI.

下面就是这个例子的代码:

Models/SampleModel.cs

using System.ComponentModel.DataAnnotations;

using System.Web.Mvc;



namespace TemplatesPart4.Models

{

    public class SampleModel

    {

        public static SampleModel Create() {

            return new SampleModel {

                Boolean = true,

                EmailAddress = "[email protected]",

                Decimal = 21.1234M,

                Integer = 42,

                Hidden = "Uneditable",

                HiddenAndInvisible = "Also uneditable",

                Html = "This is <b>HTML</b> enabled",

                MultilineText = "This\r\nhas\r\nmultiple\r\nlines",

                NullableBoolean = null,

                Password = "supersecret",

                String = "A simple string",

                Url = "http://www.cnblogs.com/lemontea",

                ChildModel = new ChildModel {

                    FirstName = "zhang",

                    LastName = "weiwen",

                }

            };

        }



        public bool Boolean { get; set; }



        [DataType(DataType.EmailAddress)]

        public string EmailAddress { get; set; }



        public decimal Decimal { get; set; }



        [HiddenInput]

        public string Hidden { get; set; }



        [HiddenInput(DisplayValue = false)]

        public string HiddenAndInvisible { get; set; }



        [DataType(DataType.Html)]

        public string Html { get; set; }



        [Required]

        [Range(10, 100)]

        public int Integer { get; set; }



        [DataType(DataType.MultilineText)]

        public string MultilineText { get; set; }



        public bool? NullableBoolean { get; set; }



        [DataType(DataType.Password)]

        public string Password { get; set; }



        public string String { get; set; }



        [DataType(DataType.Url)]

        public string Url { get; set; }



        [DisplayFormat(NullDisplayText = "(null value)")]

        public ChildModel ChildModel { get; set; }

    }

}

Models/ChildModel.cs

using System.ComponentModel.DataAnnotations;



namespace TemplatesPart4.Models

{

    [DisplayColumn("FullName")]

    public class ChildModel

    {

        [Required, StringLength(25)]

        public string FirstName { get; set; }



        [Required, StringLength(25)]

        public string LastName { get; set; }



        [ScaffoldColumn(false)]

        public string FullName {

            get {

                return FirstName + " " + LastName;

            }

        }



    }

}

Controllers/HomeController.cs

using System.Web.Mvc;

using TemplatesPart4.Models;



namespace TemplatesPart4.Controllers

{

    public class HomeController : Controller

    {

        static SampleModel model = SampleModel.Create();



        public ActionResult Index() {

            return View(model);

        }



        public ViewResult Edit() {

            return View(model);

        }



        [HttpPost]

        [ValidateInput(false)]

        public ActionResult Edit(SampleModel editedModel) {

            if (ModelState.IsValid) {

                model = editedModel;

                return RedirectToAction("Details");

            }



            return View(editedModel);

        }

    }

}

Views/Home/Index.aspx

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TemplatesPart4.Models.SampleModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">

    Index

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Index</h2>

    <fieldset style="padding: 1em; margin: 0; border: solid 1px #999;">

        <%= Html.DisplayForModel() %>

    </fieldset>

    <p>

        <%= Html.ActionLink("Edit", "Edit") %></p>

</asp:Content>

Views/Home/Edit.aspx

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TemplatesPart4.Models.SampleModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">

    Edit

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Edit</h2>

    <% using (Html.BeginForm()) { %>

    <fieldset style="padding: 1em; margin: 0; border: solid 1px #999;">

        <%= Html.ValidationSummary("Broken stuff:") %>

        <%= Html.EditorForModel() %>

        <input type="submit" value="  Submit  " />

    </fieldset>

    <% } %>

    <p>

        <%= Html.ActionLink("Details", "Index") %></p>

</asp:Content>

默认显示

运行,界面如下图,这时没有任何自定义项:

image

译注:原文中的截图会生成ChildModel,(null value),不知是不是版本原因,ASP.NET MVC 2 RTM版本的Object模版应该是不会渲染这个属性的(!metadata.IsComplexType),前面的文章和下面同理.RTM官方版本的Object模版如下:

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

<script runat="server">

    bool ShouldShow(ModelMetadata metadata) {

        return metadata.ShowForDisplay

            && metadata.ModelType != typeof(System.Data.EntityState)

            && !metadata.IsComplexType

            && !ViewData.TemplateInfo.Visited(metadata);

    }

</script>

<% if (Model == null) { %>

    <%= ViewData.ModelMetadata.NullDisplayText %>

<% } else if (ViewData.TemplateInfo.TemplateDepth > 1) { %>

    <%= ViewData.ModelMetadata.SimpleDisplayText %>

<% } else { %>

    <% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => ShouldShow(pm))) { %>

        <% if (prop.HideSurroundingHtml) { %>

            <%= Html.Display(prop.PropertyName) %>

        <% } else { %>

            <% if (!String.IsNullOrEmpty(prop.GetDisplayName())) { %>

                <div class="display-label"><%= prop.GetDisplayName() %></div>

            <% } %>

            <div class="display-field"><%= Html.Display(prop.PropertyName) %></div>

        <% } %>

    <% } %>

<% } %>

image

 

编辑页面:

image

表格布局

表格布局是最普通的名值对布局.注意编辑版本的模版在required字段的label前添加星号(*).

Views/Shared/DisplayTemplates/Object.ascx

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

<% if (Model == null) { %>

<%= ViewData.ModelMetadata.NullDisplayText %>

<% }

   else if (ViewData.TemplateInfo.TemplateDepth > 1) { %>

<%= ViewData.ModelMetadata.SimpleDisplayText %>

<% }

   else { %>

<table cellpadding="0" cellspacing="0" border="0">

    <% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForDisplay && !ViewData.TemplateInfo.Visited(pm))) { %>

    <% if (prop.HideSurroundingHtml) { %>

    <%= Html.Display(prop.PropertyName) %>

    <% }

       else { %>

    <tr>

        <td>

            <div class="display-label" style="text-align: right;">

                <%= prop.GetDisplayName() %>

            </div>

        </td>

        <td>

            <div class="display-field">

                <%= Html.Display(prop.PropertyName) %>

            </div>

        </td>

    </tr>

    <% } %>

    <% } %>

</table>

<% } %>

截图:

image

Views/Shared/EditorTemplates/Object.ascx

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

<% if (ViewData.TemplateInfo.TemplateDepth > 1) { %>

<%= ViewData.ModelMetadata.SimpleDisplayText %>

<% }

   else { %>

<table cellpadding="0" cellspacing="0" border="0">

    <% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm))) { %>

    <% if (prop.HideSurroundingHtml) { %>

    <%= Html.Editor(prop.PropertyName) %>

    <% }

       else { %>

    <tr>

        <td>

            <div class="editor-label" style="text-align: right;">

                <%= prop.IsRequired ? "*" : "" %>

                <%= Html.Label(prop.PropertyName) %>

            </div>

        </td>

        <td>

            <div class="editor-field">

                <%= Html.Editor(prop.PropertyName) %>

                <%= Html.ValidationMessage(prop.PropertyName, "*") %>

            </div>

        </td>

    </tr>

    <% } %>

    <% } %>

</table>

<% } %>

截图:

image

Shallow Dive vs. Deep Dive

在上面的截图中,ChildModel显示为"(null value)",ChildModel是一个复合model,所以所以它遵循shallow dive的原则,在我们有一个ChildModel对象前,它显示了我们在model设置的NullDisplayText特性值.

注意即使在编辑版本中,我们也不能编辑ChildModel,因为shallow dive原则阻止我们递归呈现编辑UI.

如果我们改变编辑模版,把第一个 if 语句去掉(阻止deep dive的那个),这时就会为ChildModel显示可编辑字段:

image

在SampleModel的Create()方法中加入:

ChildModel = new ChildModel {

    FirstName = "zhang",

    LastName = "weiwen",

}

截图:

image

因为我们还没有改变我们的Object显示模版,我们仍然使用shallow dive原则.另外,它显示全名是因为定义ChildModel时使用了DataAnnotations特性[DisplayColumn]来说明"当要显示这个复合对象时显示这一列",我门指定[DisplayColumn]给命名为FullName的综合属性,这个属性通常不会显示,因为我们附注了[ScaffoldColumn(false)].

如果我们改变Object的显示模版为deep dive,那么显示如下:

image

结语

这篇文章,我给你介绍了几个自定义Object模版的方法,用于显示不同的界面.包括表格布局代替行布局,required字段前添加星号,而且实现复合对象嵌套复合对象的Deep Dive场景.下一篇文章,我会怎么改变所有模版实现围绕母版页布局,灵感来自Eric Hexter的文章Opinionated Input Builders.

你可能感兴趣的:(template)