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 = "admin@contoso.com",
                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.

posted @ 2011-11-14 14:48  zhangweiwen  阅读(490)  评论(0编辑  收藏  举报