ASP.NET MVC 2 Templates, Part 4: Custom Object Templates
Series Index
- Part 1: Introduction
- Part 2: ModelMetadata
- Part 3: Default Templates
- Part 4: Custom Object Templates
- Part 5: Master Page Templates
Customizing Templates
In Part 3, we saw what the default templates would look like if we’d written them as .ascx files. In this blog post, we’ll discuss some of the customizations you can make to the Object templates to enable different features and different displays for your template-based UI.
For these examples, here are the models, controller, and views that we’ll be using.
Models/SampleModel.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
using System.ComponentModel.DataAnnotations; using System.Web.Mvc; 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
has
multiple
lines" , NullableBoolean = null , Password = "supersecret" , String = "A simple string" , }; } 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
using System.ComponentModel.DataAnnotations; [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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
using System.Web.Mvc; public class HomeController : Controller { static SampleModel model = SampleModel.Create(); public ViewResult 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
1
2
3
4
5
6
7
8
9
10
11
12
|
<%@ Page Language="C#" MasterPageFile="~/Views/shared/Site.master" Inherits="ViewPage< SampleModel >" %> < asp:Content ContentPlaceHolderID = "MainContent" runat = "server" > < h3 >Details</ h3 > < 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<%@ Page Language="C#" MasterPageFile="~/Views/shared/Site.master" Inherits="ViewPage< SampleModel >" %> < asp:Content ID = "Content2" ContentPlaceHolderID = "MainContent" runat = "server" > < h3 >Edit</ h3 > <% 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 > |
The Default Display
When we show this home controller without any customizations, this is what the details page looks like:
And this is our edit page:
Tabular Layout
One of the more commonly requested layouts is to do a tabular layout inside of the linear name/value, name/value layout that we do by default. Notice that the editor version of this layout also adds asterisks to the label for required fields.
Views/Shared/DisplayTemplates/Object.ascx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<%@ 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 > <% } %> |
Which creates this layout:
Views/Shared/EditorTemplates/Object.ascx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<%@ 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 > <% } %> |
Which creates this layout:
Shallow Dive vs. Deep Dive
In the screenshots above, ChildModel is showing as “(null value)”. ChildModel is itself a complex model, so it follows the rules for shallow dive vs. deep dive. Before we have a child model object, it’s showing the NullDisplayText as we set in the attribute in the model above.
Notice that even in edit mode above, we can’t edit the child model. That’s because the shallow dive logic prevents us from presenting a recursive editing UI.
If we change the Editor template above and remove the first “if” statement (which is what prevents the deep dive), then the editor will now show us editing fields for the child model:
And now our display shows:
Since we haven’t changed our Object Display template, we still get a shallow dive on this object. Further, it’s showing us the full name because we’ve used the DataAnnotations [DisplayColumn] attribute to say “display this property when showing this complex object in shallow form”. We’ve pointed [DisplayColumn] to a synthesized property called FullName, which we don’t normally show because we’ve annotated it with [ScaffoldColumn(false)].
If we change the Object Display template to do a deep dive, then we would see this:
Wrapping Up
In this blog post, I’ve shown you some of the ways you can customize the Object template to get different displays for your templates. That includes a tabular display instead of a linear display, adding asterisks to field names when fields are required, as well as enabling Deep Dive scenarios for complex objects inside of complex objects. In the next blog post, I’ll examine changing all the templates to enable an entirely different layout system centered around Master pages, inspired by Eric Hexter’s Opinionated Input Builders blog post series.
文章引自:http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-4-custom-object-templates.html