Share via


Exercise 2: Template Helpers in ASP.NET MVC 2

In this Exercise, you will learn how to use Templates in your ASP.NET MVC 2 application. You will begin by using the templates provided out of the box by the framework and then create and use custom templates for displaying and creating an Activity.

The Framework has nine pre-defined display templates: Boolean, Decimal, EmailAddress, HiddenInput, Html, Object, String, Text, and Url.

Note:
There is no actual difference between the Text and String template.

And seven pre-defined editor templates: Boolean, Decimal, HiddenInput, MultilineText, Object, Password, and String.

Finally, you will see how to use the UIHint attribute to define the template that should when rendering a property. The UIHint attribute sets the value of the TemplateHint property in ModelMetadata. This property has the highest priority when choosing the template to render data.

Task 0 – Exploring the Solution

In this task, you will explore the solution you obtained after completing exercise 1, to see how Views were used in the original Plan My Night application, so as to understand the benefits that MVC 2 templates provide.

  1. Open Microsoft Visual Studio 2010. Click Start | All Programs | Microsoft Visual Studio 2010 | Microsoft Visual Studio 2010.
  2. Open the solution file Begin.sln located under \Ex2-Templates\begin\ (Choosing the folder that matches the language of your preference). Alternatively, you can continue working with the solution obtained after completing exercise 1.

    Up to now, the definition of how to render the entities is spread in each view. Create and Details view have the information on how to render an Activity within them.

    Figure 9

    Exploring the PlanMyNight solution (C#)

    Figure 10

    Exploring the PlanMyNight solution (VB)

  3. In the Solution Explorer, double-click on Details.aspx, located under Views\Activities, to open it.

    Figure 11

    Details.aspx (C#)

    Figure 12

    Details.aspx (VB)

    As you can see, the Details view has the information on how to display an Activity. This approach has two big downsides; on one hand, if you wanted to display an activity in another page, you would have to duplicate the code there, while on the other hand, the View is not easy to read, is hard to understand that an activity is being rendered there.

    Note:
    DisplayFor is a Helper Method that renders the template for the given property. This method was added on the previous exercise.

    Notice the following code: <h2><span><%=Html.DisplayFor(activity=>activity.Name) %></span></h2>

    DisplayFor searches for the default string display template, which defines how to render a String (because the Name property of the Activity class is a string). This template is provided out-of-the-box with MVC 2.

  4. In the Solution Explorer, double-click on Create.aspx, located under Views\Activities, to open it.

    Figure 13

    Create.aspx (C#)

    Figure 14

    Create.aspx (VB)

    Similar as before, the Create view has the information on how to render the CreateActivityModel view model.

Task 1 – Creating a Custom Display Template for Rendering an Activity

In this task, you will create a new Activity display template, which will contain the information on how to render an activity.

  1. In the Solution Explorer, right-click on the Activities folder, located under Views, point to Add and choose New Folder.
  2. Type DisplayTemplates as the folder name.

    Note:
    When searching for the display template that should be used to render a property, there are two things that must be taken into account.

    First, you need to know where to locate the template. MVC 2 searches for the template to render as follows:

    1- DisplayTemplates folder inside the corresponding Views sub-folder (in this case, Activities)

    2- DisplayTemplates folder inside the Views\Shared folder.

    4- Uses the pre-defined templates (listed in the overview of this exercise.)

    Note: if you are using Areas, the framework first searches within the Area folder.

    The difference with the EditorTemplates, is that the framework searches for the templates inside the EditorTemplates folder instead.

    The second thing you need to take into account is the criteria that the framework uses for choosing the template to use.

    1- TemplateHint property, from the ModelMetadata (set through the UIHint attribute)

    2- DataTypeName property, from the ModelMetadata (set through the DataType attribute)

    3- The name of the Type.

    Figure 15

    DisplayTemplates Folder

  3. The first template you will create is the Rating template, to do this, right-click on the DisplayTemplates folder you have just created, point to Add and select View
  4. Make sure Create a partial view (.ascx) is checked, and type Rating as View name

    Figure 16

    Add View Rating.ascx dialog

    The Rating display template will contain the information on how to display the rating of an activity. This will clean up the View of unnecessary code that is strongly related to how the Rating is displayed.

  5. Copy the following code inside Rating.ascx:

    (Code SnippetBuilding MVC2 AppRating Display Template CSharp)

    <%
    double rating;
    double.TryParse(Model as string, out rating);
    rating = Math.Round(rating * 2) / 2;
    %>
    <p>
    <%=Html.LabelFor(m=>m) %>
    <% if(rating > 0) { %>
    <span class="rating rating_<%=rating.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture).Replace(".", "_")%>"><%=rating.ToString("0.0")%></span>

    <% } else { %>
    Awaiting at least one more vote.
    <% } %>
    </p>

    (Code SnippetBuilding MVC2 AppRating Display Template VB)

    <%
    Dim rating As Double
    Double.TryParse(Model, rating)
    rating = Math.Round(rating * 2) / 2
    %>
    <p>
    <%= Html.LabelFor(Function(m) m)%>
    <% If rating > 0 Then%>
    <span class="rating rating_<%=rating.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture).Replace(".", "_")%>"><%=rating.ToString("0.0")%></span>
    <% Else%>
    Awaiting at least one more vote.
    <% End If%>
    </p>

    Note:
    This code is similar to the code in the Details.aspx View. Later on this exercise you will remove the unnecessary code from this view.

  6. Next, you will create the Activity display template, to do this, right-click on the DisplayTemplates folder you have just created, point to Add and select View…
  7. Make sure Create a partial view (.ascx) is checked, and type Activity as View name
  8. Check the Create a strongly-typed view checkbox, and type PlanMyNight.Models.Entities.Activity(C#)PlanMyNight.Activity(VB) as View data class

    Figure 17

    Add View Activity.ascx dialog (C#)

    Figure 18

    Add View Activity.ascx dialog (VB)

    The Activity display template will contain the information on how to display the activity. For simplicity you will simply add the same code that is used in the Details.aspx View. Later on this exercise you will create a custom template to display the phone number.

  9. Copy the following code inside Activity.ascx:

    (Code SnippetBuilding MVC2 AppActivity Display Template CSharp)

    <div class="innerPanel">
    <h2><span><%=Html.DisplayFor(activity=>activity.Name) %></span></h2>
    </div>
    <div class="items">
    <%= Html.DisplayFor(activity => activity.Rating, "Rating") %>
    <p><%=Html.LabelFor(activity => activity.Street)%> <%=Html.DisplayFor(activity => activity.Street)%> | <%=Html.DisplayFor(activity => activity.City)%>, <%=Html.DisplayFor(activity => activity.State)%> <%=Html.DisplayFor(activity => activity.Zip)%> </p>
    <p><%=Html.LabelFor(activity => activity.PhoneNumber) %> <%=Html.DisplayFor(activity => activity.PhoneNumber)%></p>
    </div>

    (Code SnippetBuilding MVC2 AppActivity Display Template VB)

    <div class="innerPanel">
    <h2><span><%= Html.DisplayFor(Function(activity) activity.Name)%></span></h2>
    </div>
    <div class="items">
    <p><%= Html.DisplayFor(Function(activity) activity.Rating, "Rating") %></p>
    <p><%= Html.LabelFor(Function(activity) activity.Street)%> <%= Html.DisplayFor(Function(activity) activity.Street)%> | <%= Html.DisplayFor(Function(activity) activity.City)%>, <%= Html.DisplayFor(Function(activity) activity.State)%> <%= Html.DisplayFor(Function(activity) activity.Zip)%> </p>
    <p><%= Html.LabelFor(Function(activity) activity.PhoneNumber)%> <%= Html.DisplayFor(Function(activity) activity.PhoneNumber)%></p>
    </div>

    Notice that you have replaced the rendering of the rating from:

    <%=Html.LabelFor(activity=>activity.Rating) %>
    <% if(rating > 0) { %>
    <span class="rating rating_<%=rating.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture).Replace(".", "_")%>"><%=rating.ToString("0.0")%></span>
    <% } else { %>
    Awaiting at least one more vote.
    <% } %>

    <p><%= Html.LabelFor(Function(activity) activity.Rating)%>
    <% If (rating > 0) Then%>
    <span class="rating rating_<%=rating.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture).Replace(".", "_")%>"><%=rating.ToString("0.0")%></span>
    <% Else%>
    Awaiting at least one more vote.
    <% End If%>
    </p>

    to:

    <p><%= Html.DisplayFor(activity => activity.Rating, "Rating") %></p>

    <p><%= Html.DisplayFor(Function(activity) activity.Rating,"Rating") %></p>

    Note:
    The DisplayFor Helper Method, with the lambda expression activity => activity.Rating , indicates to the framework that the Rating property of the current Model (Activity) must be displayed. The second parameter, “Rating”, indicates to the framework which template to use.

    The framework will search for the Rating display template inside the DisplayTemplate folder and use the Rating template you have just created to display that property.

    Finally, you need to remove the code used for rendering the Activity from the Details.aspx View, and replace it with the DisplayForModel Helper method that will indicate the framework that it should render the Model (Activity) using the corresponding template (Activity.ascx).

  10. In the Solution Explorer, double-click on the Details.aspx file, located under Views\Activities, to open it.

    Note:
    DisplayForModel is equivalent to DisplayFor(model => model) which displays the HTML markup required for rendering each property in the model.

  11. Replace the MainContent <asp:Content> element with the following:

    (Code SnippetBuilding MVC2 App – Details.aspx MainContent CSharp)

    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <div class="panel">
    <%= Html.DisplayForModel() %>
    <div class="toolbox">
    <% using (Html.BeginForm("Rate","Activities",new { Id=Model.Id}, FormMethod.Post,null)) %>
    <% { %>
    | Rating:
    <%=Html.DropDownList("Rating", new[] { new SelectListItem { Text = "1" }, new SelectListItem { Text = "2" }, new SelectListItem { Text = "3" }, new SelectListItem { Text = "4" }, new SelectListItem { Text = "5" } })%>
    <input type="submit" value="Rate »" />
    <% } %>
    </div>
    </div>
    </asp:Content>

    (Code SnippetBuilding MVC2 App – Details.aspx MainContent VB)

    <asp:Content ContentPlaceHolderID="MainContent" runat="server">
    <div class="panel">
    <%= Html.DisplayForModel()%>
    <div class="toolbox">
    <% Using (Html.BeginForm("Rate", "Activities", New With {.Id = Model.Id}, FormMethod.Post, Nothing))%>
    | Rating:
    <%= Html.DropDownList("Rating", {New SelectListItem With {.Text = "1"}, New SelectListItem With {.Text = "2"}, New SelectListItem With {.Text = "3"}, New SelectListItem With {.Text = "4"}, New SelectListItem With {.Text = "5"}})%>
    <input type="submit" value="Rate »" />
    <% End Using%>
    </div>
    </div>
    </asp:Content>

Up to this point, you have completed creating a custom template for the Activity class, including a second template for the Rating property.

Notice that no change was done to the Activity class; this is possible because you are using the same name of the Property or Class when creating the Templates. By default, MVC searches for a template matching the property name when the DisplayFor Helper Method, or the model type when the DisplayForModel Helper Method are used.

Task 2 – Creating a Custom Editor Template for Rendering the ActivityCreateModel

In this task, you will create a custom editor template, which will contain the information required to render the form for creating an activity.

  1. In the Solution Explorer, right-click on the Activities folder, located under Views, point to Add and choose New Folder.
  2. Type EditorTemplates as the folder name.

    Note:
    As with the Display Template, templates are first searched in this folder.

    Figure 19

    EditorTemplates Folder

    You will create a String template, which will override the pre-definde template, to include the validation and label information altogether with the edit text box, when rendering a string.

    Note:
    This template will only override the pre-defined string template, only when rendering the edit form of a string.

  3. In the Solution Explorer, Right-click on the EditorTemplates folder you have just created, point to Add and select View
  4. Make sure Create a partial view (.ascx) is checked, and type String as View name

    Figure 20

    Add View String.ascx dialog

  5. Copy the following code inside the String.ascx template:

    (Code SnippetBuilding MVC2 AppString Template CSharp)

    <p>
    <%= Html.LabelFor(m => m) %>
    <%= Html.TextBoxFor(m=>m) %>
    <%= Html.ValidationMessageFor(m => m)%>
    </p>

    (Code SnippetBuilding MVC2 AppString Template VB)

    <p>
    <%= Html.LabelFor(Function(m) m)%>
    <%= Html.TextBoxFor(Function(m) m)%>
    <%= Html.ValidationMessageFor(Function(m) m)%>
    </p>

    Note:
     The code above uses three different Html Helpers.

    The first, LabelFor, is used to display a label of the given model; in this example, the helper renders the value of the DisplayName attribute set on the ActivityCreateModel class. By default, the helper reflects the model and prints the name of the property.

    Next, TextBoxFor, is used to display a textbox for the corresponding property. This field will allow the user to input the value for the property.

    Finally, ValidationMessageFor, will be used to render a validation error message if the user enters an invalid value. This error message is taken from the ModelState. Further details on how validation works in MVC2 can be found in the next exercise.

  6. At this point, you can go back to Create.aspx and replace the code required for rendering all the string properties to use the EditorFor Template Helper Method. Instead, you will create a Custom template for rendering the Edit form, and use this helper there.

    Note:
    The EditorFor Helper Method works as the DisplayFor Helper. It searchs for the template that should be used to render the field, and uses it. If it founds none, it will use the pre-defined templates instead. In the example, using the EditorFor Helper Method on the String properties will use the template you have just created.

  7. In the Solution Explorer, right-click on the EditorTemplates folder you created before, point to Add and select View…
  8. Make sure Create a partial view (.ascx) is checked, and type CreateActivity as View name
  9. Check the Create a strongly-typed view checkbox, and type PlanMyNight.ViewModels. ActivityCreateModel as View data class

    Figure 21

    Add View CreateActivity.ascx dialog

    The CreateActivity editor template will contain the information on how to render the form for creating an activity.

  10. Copy the following code inside the CreateActivity.ascx Editor Template:

    (Code SnippetBuilding MVC2 AppCreateActivity Template CSharp)

    <div class="fieldsA">
    <%= Html.EditorFor(m => m.Name) %>
    <%= Html.EditorFor(m => m.PhoneNumber) %>
    <p>
    <%= Html.LabelFor(m => m.State) %>
    <%= Html.DropDownListFor(m => m.State, Model.States)%>
    <%= Html.ValidationMessageFor(m => m.State)%>
    </p>
    <%= Html.EditorFor(m => m.Zip) %>
    </div>
    <div class="fieldsB">
    <%= Html.EditorFor(m => m.Street) %>
    <%= Html.EditorFor(m => m.City) %>
    <p>
    <%= Html.LabelFor(m => m.ActivityTypeId)%>
    <%= Html.DropDownListFor(m => m.ActivityTypeId, Model.ActivityTypes)%>
    <%= Html.ValidationMessageFor(m => m.ActivityTypeId)%>
    </p>
    </div>

    (Code SnippetBuilding MVC2 AppCreateActivity Template VB)

    <div class="fieldsA">
    <%= Html.EditorFor(Function(m) m.Name)%>
    <%= Html.EditorFor(Function(m) m.PhoneNumber)%>
    <p>
    <%= Html.LabelFor(Function(m) m.State)%>
    <%= Html.DropDownListFor(Function(m) m.State, Model.States)%>
    <%= Html.ValidationMessageFor(Function(m) m.State)%>
    </p>
    <%= Html.EditorFor(Function(m) m.Zip)%>
    </div>
    <div class="fieldsB">
    <%= Html.EditorFor(Function(m) m.Street)%>
    <%= Html.EditorFor(Function(m) m.City)%>
    <p>
    <%= Html.LabelFor(Function(m) m.ActivityTypeId)%>
    <%= Html.DropDownListFor(Function(m) m.ActivityTypeId, Model.ActivityTypes)%>
    <%= Html.ValidationMessageFor(Function(m) m.ActivityTypeId)%>
    </p>
    </div>

    Note:
    Notice that you have added the EditorFor Helper. As said before, this Helper will find the String EditorTemplate you previously created and render the three fields: label, textbox and validationmessage.

    At this point, you have completed creating the EditorTemplates; in order for your web application to use them, you will replace the Create.aspx code used to render all fields, with the EditorForModel Helper. In this case, you will use the overload that receives the Template name as parameter.

    Note:
    By using the overload of the EditorForModel Helper that receives the TemplateName as parameter, you will be able to force the framework to use a certain Template.

  11. In the Solution Explorer, double-click on Create.aspx, located under Views\Activities, to open it.
  12. Replace the <fieldset> definition with the following code:

    (Code SnippetBuilding MVC2 App –Create.aspx Fieldset CSharp)

    <fieldset>
    <%= Html.EditorForModel("CreateActivity") %>
    <p>
    <input type="submit" value="Save" />
    </p>
    </fieldset>

Once more, you have created the editor templates required for creating a new Activity, without modifying the code.

Task 3 – Using the UIHint Attribute for Rendering the Phone Number

In this task, you will learn how to use the UIHint Attribute to let the MVC framework know which template to use when rendering a property.

  1. In the Solution Explorer, right-click on the DisplayTemplates folder, located under Views\Activities, point to Add and select View…
  2. Make sure Create a partial view (.ascx) is checked, and type PhoneNumber as View name. Since PhoneNumber render strings, leave Create strongly-typed view unchecked. 

    Figure 22

    Add View PhoneNumber.ascx dialog

    The PhoneNumber display template will contain the information on how to format a string, to render it as if it were a phone number.

  3. Add the following code, to render the string with the pattern (###) ###-####, only when the digit count is ten.

    Note:
    You could provide different formatting option, this is just an example to show the possibilities that MVC 2 Templates provide.

    (Code SnippetBuilding MVC2 AppPhoneNumber Template CSharp)

    <%
    long numericPhone;
    var phone = (Model as string) ?? string.Empty;
    long.TryParse(phone, out numericPhone);
    %>

    <% if ((numericPhone != 0) && (phone.Length == 10)){ %>
    <%= Html.Encode(numericPhone.ToString("(###) ###-####")) %>
    <%} else {%>
    <%= Html.Encode(phone)%>
    <%} %>

    (Code SnippetBuilding MVC2 AppPhoneNumber Template VB)

    <%
    Dim numericPhone As Long
    Dim phone = If(TryCast(Model, String), String.Empty)
    Long.TryParse(phone, numericPhone)
    %>

    <% If ((numericPhone <> 0) And (phone.Length = 10)) Then%>
    <%= Html.Encode(numericPhone.ToString("(###) ###-####")) %>
    <% Else%>
    <%= Html.Encode(phone)%>
    <% End If%>

  4. Next, you will decorate the Activity’s PhoneNumber property with the UIHint attribute in order to let the framework know that it must use the PhoneNumber template when rendering this property. To do this, double click on the ActivityMetadata class, located under Models\Entities, to open it.
  5. Replace the attributes decorating the PhoneNumber property with the following:[DisplayName("Phone:")]
    [UIHint("PhoneNumber")]
    public object PhoneNumber { get; set; }

    <DisplayName("Phone:")>
    <UIHint("PhoneNumber")>
    Public Property PhoneNumber As Object

Exercise 2: Verification

You have completed creating the templates, and configuring your application to use them. In this verification, you will go through the solution to check that it’s still rendering the Display and Create pages correctly. And to see how PhoneNumber is now rendered when displaying an activity.

  1. Press CTRL+F5 to run the solution without debugging.
  2. You will open the detailed information for a specific Activity to review the results of the work done. To do this, edit your browser’s address bar to navigate to https://localhost:50000/Activities/Details/49.
  3. You should notice that the usage of templates is transparent for the user, unless you intentionally change something, as done with the PhoneNumber. Notice that it is rendered following the pattern (###) ###-####

    Figure 23

    PlanMyNight Activity details using Display Templates

  4. Next, you will open the create activity view to verify the correct rendering of the page. Browse to: https://localhost:50000/Activities/Create.

    Figure 24

    PlanMyNight Create Activity using Display Templates

    Note:
    Since the template you have created has no UI changes, you will see no difference between rendering the page with Templates than without them. Nevertheless, by adding the EditorTemplates you have cleared your View’s code.