📜 ⬆️ ⬇️

Templates for displaying and editing data in ASP.NET MVC 2

Translation of a series of articles on display and editing templates.

  1. Introduction to templates. ( Original )
  2. Metadata in templates. ( Original )
  3. Built-in templates. ( Original )
  4. Create your own templates. ( Original )
  5. 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):


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:

Display Template Example

Html.Editor


Like Html.Display, there are also three methods that are used to generate html to edit an object:


If I change the DisplayForModel method to EditorForModel in my View, the page will look like this:

Editing Template Example
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 :

Line pattern substitution method
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:

Example of swapped lines
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 expressions

The 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 Metadata


Methods

The default value for SimpleDisplayText follows these rules:

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:


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:


(Replace DisplayTemplates with EditorTemplates for editing templates.)

Pattern Names


Template names are searched in the following order:

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:

Default display template
The page for editing will look like this:

Default Editing Template

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:

Replaced display pattern

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:

Replaced Editing Template

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:

Deep immersion in the editing template
And in view mode:

Shallow immersion in the display pattern
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 - - . , , .

Source: https://habr.com/ru/post/81429/


All Articles