Translation of a series of articles on display and editing templates.
- Introduction to templates. ( Original )
- Metadata in templates. ( Original )
- Built-in templates. ( Original )
- Create your own templates. ( Original )
- Master templates. ( Original )
Part one. Introduction
Introduction to templates
One of the main innovations in ASP.NET MVC is templates.
')
Templates are similar to Dynamic Data in classic ASP.NET. For an existing object of this type, the system automatically generates markup for displaying or editing it, regardless of whether the object is simple data (integer, decimal, string, etc.), or is a class representative.
Html.Display
In order to display an element, three display methods are used (each of which has several overloads):
- String based : <% = Html.Display ("PropertyName")%>
- Expression based : <% = Html.DisplayFor (model => model.ProeprtyName)%>
- Model : <% = Html.DisplayForModel ()%>
The 1st method can be used to display data from both ViewData and from a model whose type you may not know.
The following method is mainly used to transfer data from the model. In addition, this method can be used, for example, completely ignoring the values ​​of the source data (model => someOtherValue).
And the last method is a helper for the current model. The DisplayForModel method is equivalent to DisplayFor (model => model).
Let's start the examples from the model:
public class Contact
{
public string FirstName {get; set; }
public string LastName {get; set; }
public int Age {get; set; }
}
... then action:
pubilc ViewResult Details ([DefaultValue (0)] int id)
{
return View (contact [id]);
}
... and finally View:
<% @ Page language = "C #"
MasterPageFile = "~ / Views / Shared / Site.master"
Inherits = "System.Web.Mvc.ViewPage"%>
<asp: Content ContentPlaceHolderID = "MainContent" runat = "server">
<% = Html.DisplayForModel ()%>
</ asp: Content>
As a result, we get the following page:

Html.Editor
Like Html.Display, there are also three methods that are used to generate html to edit an object:
- String based : <% = Html.Editor ("PropertyName")%>
- Expression based : <% = Html.EditorFor (model => model.ProeprtyName)%>
- Model : <% = Html.EditorForModel ()%>
If I change the DisplayForModel method to EditorForModel in my View, the page will look like this:

As you can see, the system generated text boxes for strings and for integer values.
What really happens?
The template system in MVC 2 contains several built-in templates.
In order to display an object, the system uses reflection to search for all properties of the object, then generates markup for each of the properties.
If you do not use templates, the markup may look something like this:
<% foreach (var prop in ViewData.ModelMetadata.Properties) {%>
<div class = "display-label"> <% = prop.GetDisplayName ()%> </ div>
<div class = "display-field"> <% = Html.Display (prop.PropertyName)%> </ div>
<%}%>
It should be borne in mind that now it is not yet a complete implementation. I will tell you what is missing here later.This is the basis of the template for displaying a complex object: go through all the properties of the current model, show the label for each of them and call the Html.Display () method to display the property value.
What happens when we want to show a string? And about the following happens:
<% = Html.Encode (Model)%>
And again, this is not real code. What actually happens is discussed later.Pattern substitution
There are many advantages of built-in templates, but perhaps the most important is the ability to replace them anywhere in the rendering.
Let's look at an example of pattern substitution for strings. To do this, create a partial view, which we call String and place it in the
~ / Views / ControllerName / DisplayTemplates folder :

In the file we write:
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<% = Html.Encode (Model)%> <em> Hello everyone! </ Em>
Updating the page we will see:

As you can see, the new template was automatically substituted and we did not need to look through all our lines. However, the template has also been applied to the Age property, the type of which is actually an integer, but we will talk about this a little later.
If we need to write a template for editing, it must be placed in the
EditorTemplates folder.
Part two. Modelmetadata
Understanding the model
One of the new classes that appeared along with ASP.NET MVC 2 is Model Metadata. This class is designed to tell you the necessary information about the object to display it. And although this is mainly used for writing templates, metadata is always available, not only in templates.
What is a Model?
In the context of using ModelMetadata, the definition of “Model” is a little blurred.
Consider again the model from the previous part:
public class Contact
{
public string FirstName {get; set; }
public string LastName {get; set; }
public int Age {get; set; }
}
We have created a strongly-typed representation for this model. If in this view you refer to ViewData.ModelMetadata, then the model in this case will be an object of class Contact.
However, using the metadata object you can get information about all the properties of the object. You can get a collection of ModelMetadata objects for each of the properties. In the context of our example, we get 3 new meta-objects, for FirstName, LastName and Age. If you look at the metadata for the FirstName property, you will see that the data type of the model is
string , and the type of the container object is
Contact . Thus, you can iterate through all the properties of a complex object recursively.
Where can I get ModelMetadata?
Using the current model.The easiest way to access metadata is to use the ViewData property. In this case, ModelMetadata describes the model represented in ViewData. In the case of rendering a template for an object, this method is the easiest.
Get one of the metadata properties you already have.If you already have a metadata object, you can use the Properties property, which returns a list of ModelMetadata objects for each property.
Using expressionsThe ModelMetadata class has two static methods: FromStringExpression and FromLambdaExpression. These methods are used when we want to obtain from the expression (for example, “PropertyName” or “m => m.PropertyName”) the corresponding metadata. Most existing HTML helpers have already been rewritten within these two methods.
What do they have inside?
Before speaking on how to work with ModelMetadata, let's see what information the ModelMetadata class object provides.
Properties related to the model and its container:- Model and ModelType
Returns the model and model type. And although the value may be null, we can still know the data type.
- ContainerType and PropertyName
Returns the type of the object containing the property in question and the name of the property. Not all
models represent properties, so these properties can be null.
- Properties
Returns a collection of ModelMetadata objects that describe the properties of the current model.
Model Metadata- ConvertEmptyStringToNull
A flag indicating whether to convert the empty string received from the client to null.
Default: true - DataTypeName
A string that can be used to obtain information about the type of data (for example, to find out if the current string is an email address). Frequently used data type names are EmailAddress, Html, Password, and Url.
Default: null - Description
Description of the model.
Default: null - DisplayFormatString
Format for displaying the value of the model in the template.
Default: null - DisplayName
The display name of the model.
Default: null - EditFormatString
The format of the string that will be used to edit the value in the template.
Default: null - HideSurroundingHtml
A flag indicating that the current field should not be accompanied by any html tags (for example, a label). Often used to generate hidden fields
Default: false - IsComplexType
A flag indicating whether the system should treat the type as complex (and therefore use a template for a complex type), or as a simple type (for example, string).
User is not installed. - IsNullableValueType
Flag indicating whether the type is Nullable /
User is not installed. - IsReadOnly
Flag indicating that this property is read only.
Default: false - IsRequired
Is the property required.
Default: true for non-nullable types; false for all others. - NullDisplayText
The text to display if the model is null.
Default: null - ShortDisplayName
Short name for the current model. Designed for use in table headers. If this property is not set, then the DisplayName property is used.
Default: null - ShowForDisplay
Should the current model be used for viewing.
Default: true - ShowForEdit
Should the current model be used for editing
Default: true - Simpledisplaytext
The text that should be displayed for a given model in the row of totals. Otherwise, the display of the complex object is used.
Default: see below - TemplateHint
Hint used for the model.
Default: null - Watermark
Text that can be displayed as a hint in the edit box.
Default: null
Methods- GetDisplayName ()
This method can be used to get the model name. Returns the value of the DisplayName property if it is not null. If PropetyName is not null, then returns it. Else returns ModelType.Name. - GetValidators ()
This method is used to obtain validators for this model. They can be used both for server validation and for generating client validation rules.
The default value for SimpleDisplayText follows these rules:- If Model is null, NullDisplayText is returned.
- If the method of the ToString model is overloaded, then the result of its call is returned.
- If the model has no properties, it returns a String.Empty
- If the first property of the model is null, NullDisplayText is returned.
- Otherwise, the ToString () of the first property is returned.
How is ModelMetadata formed?
We added a custom metadata system in ASP.NET MVC 2. By default, meta objects are generated based on data obtained from attributes.
The following attributes are used to form the metadata model:
- [HiddenInput] (System.Web.Mvc)
A property marked with this attribute will generate a “hidden” field in edit mode. By default, in this case also accompanying html-tags will not be used. If you set the DisplayValue flag to true, the accompanying html and “hidden” field will be generated. In addition, in this case, the TemplateHint property is set (which can be covered by the [UIHint] attribute)
- [UIHint] (System.ComponentModel.DataAnnotations)
Sets the TemplateHint property.
- [DataType] (System.ComponentModel.DataAnnotations)
Sets the DataTypeName property.
- [ReadOnly] (System.ComponentModel)
Sets the IsReadOnly property. Please note that any property without a setter will be automatically marked with the [ReadOnly] attribute.
- [DisplayFormat] (System.ComponentModel.DataAnnotations)
With this attribute, you can set the NullDisplayText for metadata. If you set the DataFormatString attribute property, it will be applied to the DisplayFormatString property of the metadata. If you set the attribute attribute ApplyFormatInEditMode to true, then it will also apply to EditFormatString. Setting the ConvertEmptyStringToNull property will affect the ConvertEmptyStringToNull metadata property.
- [ScaffoldColumn] (System.ComponentModel.DataAnnotations)
Set the properties ShowForDisplay and ShowForEdit
- [DisplayName] (System.ComponentModel)
Used for the DisplayName property.
Part Three Built-in templates.
The approach to the use of templates
Before talking about the built-in templates, we will spend a few minutes explaining how the templates are used in the work, how to directly override the template.
Path
When it is decided which template to use, the system looks in several ways. For each of them, it searches for an ascx file named “DisplayNames / TemplateName” or “EditorName / TemplateName”, depending on what result you want.
The search takes place in the following order:
- ~ / Areas / AreaName / Views / ControllerName / DisplayTemplates / TemplateName.aspx and .ascx
- ~ / Areas / AreaName / Views / Shared / DisplayTemplates / TemplateName.aspx and .ascx
- ~ / Views / ControllerName / DisplayTemplates / TemplateName.aspx and .ascx
- ~ / Views / Shared / DisplayTemplates / TemplateName.aspx and .ascx
(Replace DisplayTemplates with EditorTemplates for editing templates.)
Pattern Names
Template names are searched in the following order:
- The value of the TemplateHint property of the ModelMetadata object
- Value of the DataTypeName property of the ModelMetadata object
- Type name
- If the object is a simple type, then "String"
- If the object is a complex type and interface, then “Object”
- If the object is a complex type and not an interface, then recursively, through the entire hierarchy of nested properties, tries to get a name for each property.
When searching by type name, it means a simple name (for example, Type.Name), without a namespace. In addition, if the type is Nullable <T>, the search is performed for type T (that is, the Boolean pattern will be used for both “bool” and “Nullable <bool>”). That is, if you write a template for a value-type, you need to consider whether the type is nullable. Below is an example with a built-in template for the type Boolean.
TemplateInfo class
And the last thing you should say before delving into the implementation is the TemplateInfo class. TemplateInfo is not always available from ViewData, unlike model metadata, only when you are in a template.
The main property used in TemplateInfo is FormattedModelValue. The value of this field is either a correctly formatted value of the model, as a string (based on the format specified in ModelMetadata), or the original value of the model (if the format of the string is not specified).
There are a few more things that we will also use (for example, the TemplateDepth property and the Visited method). But I will explain their meaning as I become acquainted with them.
Built-in display templates
In total there are 9 template names that are used by the system to display data: “Boolean”, “Decimal”, “EmailAddress”, “HiddenInput”, “Html”, “Object”, “String”, “Text” and “Url”. Two of them ("Text" and "String") are implemented the same way. Some of them have corresponding analogues for editing templates, some do not.
The built-in templates in ASP.NET MVC are implemented in code, but for example, I rewrote their functionality in .ascx files. This will help you to better understand them, and it will be easier for you to write your own version of any of these templates.
DisplayTemplate / String.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<% = Html.Encode (ViewData.TemplateInfo.FormattedModelValue)%>
No surprises: just display the model, replacing the special html characters in it.
DisplayTemplates / Html.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<% = ViewData.TemplateInfo.FormattedModelValue%>
This template is even slightly simpler than the previous one, because the “Html” type implies that the content will be in the form of html and therefore should not be encoded.
Be careful when using this type if data is received from the end user, in order to avoid XSS attacks!DisplayTemplates / EmailAddress.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<a href="mailto:<%= Html.AttributeEncode(Model) %> "> <% = Html.Encode (ViewData.TemplateInfo.FormattedModelValue)%> </a>
This template assumes that your model is an email address, and automatically creates a link to it. Note that the model is used for the email address, and FormattedModelValue for display.
DisplayTemplates / Url.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<a href="mailto:<%= Html.AttributeEncode(Model) %> "> <% = Html.Encode (ViewData.TemplateInfo.FormattedModelValue)%> </a>
It works like the previous example.
DisplayTemplates / HiddenInput.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<% if (! ViewData.ModelMetadata.HideSurroundingHtml) {%>
<% = Html.Encode (ViewData.TemplateInfo.FormattedModelValue)%>
<%}%>
This template is used in pairs with the [HiddenInput] attribute (described above). It will generate the displayed value only if the user does not explicitly specify the HideSurroundHtml property.
DisplayTemplates / Decimal.ascx
<% @ control language = "C #" inherits = "System.Web.Mvc.ViewUserControl"%>
<script runat = "server">
private object FormattedValue
{
get
{
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelMetadata.Model)
{
return String.Format (System.Globalization.CultureInfo.CurrentCulture, "{0: 0.00}", ViewData.ModelMetadata.Model);
}
return ViewData.TemplateInfo.FormattedModelValue;
}
}
</ script>
<% = Html.Encode (FormattedValue)%>
This pattern displays a decimal value, with two decimal places, since this type is mainly used for monetary representations. Pay attention to the condition of such formatting.
DisplayTemplates / Boolean.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<script runat = "server">
private bool? ModelValue
{
get
{
bool? value = null;
if (ViewData.Model! = null)
{
value = Convert.ToBoolean (ViewData.Model, System.Globalization.CultureInfo.InvariantCulture);
}
return value;
}
}
</ script>
<% if (ViewData.ModelMetadata.IsNullableValueType) {%>
<select class = "list-box tri-state" disabled = "disabled">
<option value = "" <% = ModelValue.HasValue? "": "selected = 'selected'"% >>
Not Set </ option>
<option value = "true" <% = ModelValue.HasValue && ModelValue.Value? "selected = 'selected'": ""% >>
True </ option>
<option value = "false" <% = ModelValue.HasValue &&! ModelValue.Value? "selected = 'selected'": ""% >>
False </ option>
</ select>
<%} else {%>
<input class = "check-box" disabled = "disabled" type = "checkbox"
<% = ModelValue.Value? "checked = 'checked'": ""%> />
<%}%>
The template for Boolean is interesting because it needs to generate markup depending on whether the type is nullable or not. For non-nullable, the usual checkbox is used, otherwise a drop-down list with three values ​​is displayed.
DisplayTemplates / Object.ascx
It is worth explaining the logic of this template, before we consider the code, since a lot of work is done on your behalf.
The main purpose of the template for the object type is all the properties of a complex object, along with the html markup for each. In addition, he is responsible for displaying the value of the model NullDisplayText, if its value is null. And provides the so-called "shallow dive" (shallow dive), displaying only the desired level of properties. Later we will talk about setting up this template, including the preparation of the operation “deep dive” (deep dive).
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<% 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 => pm.ShowForDisplay &&! ViewData.TemplateInfo.Visited (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>
<%}%>
<%}%>
<%}%>
Let's look at this example in more detail.
<% if (Model == null) {%>
<% = ViewData.ModelMetadata.NullDisplayText%>
<%}
At this point, everything is clear, NullDisplayText is output, in case the model is null.
else if (ViewData.TemplateInfo.TemplateDepth> 1) {%>
<% = ViewData.ModelMetadata.SimpleDisplayText%>
<%}
In this place we limit the level of nesting of properties (“shallow dive”). The TemplateInfo class tracks the nesting depth of templates. For the top level, the TemplateDepth property is 1.
else {%>
<% foreach (var prop in ViewData.ModelMetadata.Properties.Where (pm => pm.ShowForDisplay
&&! ViewData.TemplateInfo.Visited (pm))) {%>
This is the main loop through the properties of the object. Properties that indicate that they should not be displayed are ignored. Another part of the properties filter, asks TemplateInfo if this object was rendered earlier, in order to prevent endless recursion.
<% if (prop.HideSurroundingHtml) {%>
<% = Html.Display (prop.PropertyName)%>
<%}
If this property is set to true, then the template simply asks the property to be displayed, without unnecessary html markup.
<% if (! String.IsNullOrEmpty (prop.GetDisplayName ())) {%>
<div class = "display-label"> <% = prop.GetDisplayName ()%> </ div>
<%}%>
<div class = "display-field"> <% = Html.Display (prop.PropertyName)%> </ div>
At this point, it is checked whether the property has a display name and, if it has one, it is output framed by the div. It then starts rendering for the property.
Built-in editing templates
Editing templates are a bit more complicated than display templates because they include the ability to edit values. They are based on existing HTML helpers. In total there are 7 editing templates: “Boolean”, “Decimal”, “HiddenInput”, “MultilineText”, “Object”, “Password” and “String”.
Often, empty strings will be passed as an element name in html-helpers. This is usually wrong, but in the case of templates, we have the “context” of the property. With this, it is easier for us to work with complex objects, since we can preserve the hierarchy of nesting properties (for example, "Contact.HomeAddress.City").
When you pass the name to the html helper, you kind of say "give me a textbox to edit the properties of an object called City." But what happens if your template is not for an address, as a complex object, but for a city, as for a string? If you pass an empty string as a name, then you say to the html-helper "give me a textbox to edit myself."
EditorTemplates / String.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<% = Html.TextBox ("", ViewData.TemplateInfo.FormattedModelValue,
new {@class = "text-box single-line"})%>
And again, let's start with the line, because this is the simplest pattern to understand. This template tells the system that it wants to receive a textbox (for editing itself), and we initiate it with the formatted value of the property being edited. Additionally, for this textbox we want to use two CSS text-box and single-line CSS.
EditorTemplates / Password.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<% = Html.Password ("", ViewData.TemplateInfo.FormattedModelValue,
new {@class = "text-box single-line password"})%>
The password entry pattern is similar to the string pattern, except that we call the Password helper method and use another CSS class (“password”).
EditorTemplates / MultilineText.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<% = Html.TextArea ("", ViewData.TemplateInfo.FormattedModelValue.ToString (),
0, 0, new {@class = "text-box multi-line"})%>
And again, without any surprises. Call the TextArea method, specify the number of rows and columns to be 0 (since we use CSS), and instead of the class “single-line” we use the class “multi-line”.
EditorTemplates / HiddenInput.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<script runat = "server">
private object ModelValue
{
get
{
if (Model is System.Data.Linq.Binary)
{
return Convert.ToBase64String (((System.Data.Linq.Binary) Model) .ToArray ());
}
if (Model is byte [])
{
return Convert.ToBase64String ((byte []) Model);
}
return model;
}
}
</ script>
<% if (! ViewData.ModelMetadata.HideSurroundingHtml) {%>
<% = Html.Encode (ViewData.TemplateInfo.FormattedModelValue)%>
<%}%>
<% = Html.Hidden ("", ModelValue)%>
As you can see, it is much more difficult than in the template to display. The ModelValue property determines whether a model is a byte array or a binary linq2sql object, and converts them to a string using base64. Then writes the result to a hidden field.
EditorTemplates / Decimal.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<script runat = "server">
private object ModelValue
{
get
{
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelMetadata.Model)
{
return String.Format (System.Globalization.CultureInfo.CurrentCulture,
"{0: 0.00}", ViewData.ModelMetadata.Model);
}
return ViewData.TemplateInfo.FormattedModelValue;
}
}
</ script>
<% = Html.TextBox ("", ModelValue, new {@class = "text-box single-line"})%>
The Decimal template is similar to the corresponding template for display, except that a textbox is generated for editing.
EditorTemplates / Boolean.ascx
<% @ control language = "C #" inherits = "System.Web.Mvc.ViewUserControl"%>
<script runat = "server">
private List <SelectListItem> TriStateValues
{
get
{
return new List <SelectListItem> {
new SelectListItem {Text = "Not Set",
Value = String.Empty,
Selected =! Value.HasValue},
new SelectListItem {Text = "True",
Value = "true",
Selected = Value.HasValue && Value.Value},
new SelectListItem {Text = "False",
Value = "false",
Selected = Value.HasValue &&! Value.Value},
};
}
}
private bool? Value
{
get
{
bool? value = null;
if (ViewData.Model! = null)
{
value = Convert.ToBoolean (ViewData.Model,
System.Globalization.CultureInfo.InvariantCulture);
}
return value;
}
}
</ script>
<% if (ViewData.ModelMetadata.IsNullableValueType) {%>
<% = Html.DropDownList ("", TriStateValues, new {@class = "list-box tri-state"})%>
<%} else {%>
<% = Html.CheckBox ("", Value ?? false, new {@class = "check-box"})%>
<%}%>
This template is also similar to the corresponding template for display.
EditorTemplates / Object.ascx
<% @ Control Language = "C #" Inherits = "System.Web.Mvc.ViewUserControl"%>
<% if (ViewData.TemplateInfo.TemplateDepth> 1) {%>
<% = ViewData.ModelMetadata.SimpleDisplayText%>
<%}
else {%>
<% foreach (var prop in ViewData.ModelMetadata.Properties.Where (pm => pm.ShowForEdit
&&! ViewData.TemplateInfo.Visited (pm))) {%>
<% if (prop.HideSurroundingHtml) {%>
<% = Html.Editor (prop.PropertyName)%>
<%} else {%>
<% if (! String.IsNullOrEmpty (Html.Label (prop.PropertyName) .ToHtmlString ())) {%>
<div class = "editor-label"> <% = Html.Label (prop.PropertyName)%> </ div>
<%}%>
<div class = "editor-field">
<% = Html.Editor (prop.PropertyName)%>
<% = Html.ValidationMessage (prop.PropertyName, "*")%>
</ div>
<%}%>
<%}%>
<%}%>
Likewise, this template has hardly changed, only the call to the ValidationMessage method was added.
Part Four. Creating your own templates
For further examples, the following model, controller and presentation will be used:
Models / SampleModel.cs
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 \ r \ nhas \ r \ nmultiple \ r \ nlines",
NullableBoolean = null,
Password = "supersecret",
String = "A simple string",
Url = "http://www.microsoft.com/",
};
}
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;
[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;
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
<% @ 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
<% @ 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>
Default display template
If we run the application with the default templates, we will see this page:

The page for editing will look like this:

We use tables
Let's make our markup in the form of a table so that the name / value pair is displayed in one line. Please note that asterisks for the required fields are displayed in the template for editing.
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>
<%}%>
As a result, we obtain the following form:

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>
<%}%>
So we made this markup:

Shallow and deep dive (shallow dive vs. deep dive)
In the screenshot above, the ChildModel is displayed as (null value), because we set this value using an attribute in the code.
Please note that even in edit mode, we cannot change this property.
If we change the editing template, removing the first “if” condition in it, we get the following:

And in view mode:

Since we have not changed the pattern to display, we still get the result with the logic of “shallow dive” (shallow dive).
The full name is shown because we use the [DisplayColumn] attribute for the model, with the help of which we indicated that the FullName property should be displayed..
If we change the display pattern to use “deep dive”, we get the following:
Part Five MasterPage Templates
The first thing we need to do is determine the MasterPage to be used for the template. One for the edit template and one for the display template.DisplayTemplates / Template.master
<% @ Master Language = "C #" Inherits = "System.Web.Mvc.ViewMasterPage"%>
<script runat = "server">
protected override void OnInit (EventArgs e) {
base.OnInit (e);
if (ViewData.ModelMetadata.HideSurroundingHtml) {
TablePlaceholder.Visible = false;
}
else {
Controls.Remove (Data);
DataPlaceholder.Controls.Add (Data);
}
}
</ script>
<asp:ContentPlaceHolder runat="server" id="Data" />
<asp:PlaceHolder runat="server" id="TablePlaceholder">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td style="width: 10em;">
<div class="display-label" style="text-align: right;">
<asp:ContentPlaceHolder runat="server" id="Label">
<%= ViewData.ModelMetadata.GetDisplayName() %>
</asp:ContentPlaceHolder>
</ div>
</td>
<td>
<div class="display-field">
<asp:PlaceHolder runat="server" id="DataPlaceholder" />
</ div>
</td>
</ tr>
</table>
</asp:PlaceHolder>
ContentPlaceHolder-, «Label» «Data». «Label» , «Data», , .
-. OnInit , html-, .
EditorTemplates/Template.master
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
<script runat="server">
protected override void OnInit(EventArgs e) {
base.OnInit(e);
if (ViewData.ModelMetadata.HideSurroundingHtml) {
TablePlaceholder.Visible = false;
}
else {
Controls.Remove(Data);
DataPlaceholder.Controls.Add(Data);
}
}
</ script>
<asp:ContentPlaceHolder runat="server" id="Data" />
<asp:PlaceHolder runat="server" id="TablePlaceholder">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td style="width: 10em;">
<asp:ContentPlaceHolder runat="server" id="Label">
<div class="editor-label" style="text-align: right;">
<%= ViewData.ModelMetadata.IsRequired ? "*" : "" %>
<%= Html.Label("") %>
</ div>
</asp:ContentPlaceHolder>
</td>
<td>
<div class="editor-field">
<asp:PlaceHolder runat="server" id="DataPlaceholder" />
<asp:ContentPlaceHolder runat="server" ID="Validation">
<%= Html.ValidationMessage("", "*") %>
</asp:ContentPlaceHolder>
</ div>
</td>
</ tr>
</table>
</asp:PlaceHolder>
, placeholder, .
- <asp:Content ...>
DisplayTemplates/String.aspx
<%@ Page Language="C#" MasterPageFile="Template.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ContentPlaceHolderID="Data" runat="server">
<%= Html.Encode(ViewData.TemplateInfo.FormattedModelValue) %>
</asp:Content>
EditorTemplates/String.aspx
<%@ Page Language="C#" MasterPageFile="Template.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ContentPlaceHolderID="Data" runat="server">
<%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue,
new { @class = "text-box single-line" }) %>
</asp:Content>
, -. .
DisplayTemplates/Object.aspx
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<% if (ViewData.TemplateInfo.TemplateDepth > 1) { %>
<%= ViewData.ModelMetadata.SimpleDisplayText %>
<% } else { %>
<% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForDisplay && !ViewData.TemplateInfo.Visited(pm))) { %>
<%= Html.Display(prop.PropertyName) %>
<% } %>
<% } %>
EditorTemplates/Object.aspx
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<% if (ViewData.TemplateInfo.TemplateDepth > 1) { %>
<%= ViewData.ModelMetadata.SimpleDisplayText %>
<% } else { %>
<% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm))) { %>
<%= Html.Editor(prop.PropertyName) %>
<% } %>
<% } %>
, MasterPage, , . .
Conclusion
ASP.NET MVC 2 - - . , , .