In April, I did a comprehensive blog post about the Html.EditorFor() and Html.DisplayFor() helpers in MVC 2, and there use with templates. It turns out I missed quite a cool feature added in the MVC 2 RTM release in March.
Taken from the release notes:
ASP.NET MVC 2 now includes new overloads of the EditorFor and DisplayFor methods. These overloads contain a parameter that accepts an anonymous object that can be used to provide extra view data. The view data provided in this parameter is merged with any existing view data that is passed to the template.
This new overload is very handy for providing additional information to your template without any nasty hacks. In this post we are going to continue our Employee example from my MVC Templates and MVC Model Binders posts, we will change the code to make use of the new overload.
Download the code for this exampleContents
New Requirement
For this example, we have been asked to add the functionality to optionally supply help text for our Offices Select field. The template will be used by different views soon, and therefore the help text will have different meanings depending on what view is using the template, so we can't hard code it.
Changes to the Index View
Change the Index view (\Views\Employee\Index.aspx), so that the call to Html.EditorFor for Offices passes an anonymous object specifying the help text for the Select input field.
Your view should look similar to:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<%MvcTempDemo1.Models.EmployeeModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Index</h2>
<% using (Html.BeginForm("Details","Employee")) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<%: Html.EditorFor(model => model.FirstName) %>
<%: Html.EditorFor(model => model.LastName) %>
<%: Html.EditorFor(model => model.DateOfBirth) %>
<%: Html.EditorFor(model => model.Offices, new {HelpText="To select multiple offices press 'Ctrl' and select the option"}) %>
<p><input type="submit" value="Create" /></p>
</fieldset>
<% } %>
<div><%: Html.ActionLink("Back to List", "Index") %></div>
</asp:Content>
Changes to the Offices Template
Next we need to change the Offices template (\Views\Shared\EditorTemplates\Offices.ascx) to handle the additional view data.Your template should look similar to the example below:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<MvcTempDemo1.Models.Office>>" %>
<%var helpText = ViewData["HelpText"] ?? string.Empty;%>
<div class="editor-help"><%:helpText%></div>
<div class="editor-label">
<%: Html.LabelFor(model => model) %>
</div>
<div class="editor-field">
<%: Html.ListBoxFor(model => model, new SelectList(Model, "OfficeId", "Country"))%>
<%: Html.ValidationMessageFor(model => model) %>
</div>
CSS Changes
Add the following CSS to \Content\Site.css:.editor-help
{
width: 32em;
min-height: 1.5em;
padding: 3px 0 0 25px;
background: #ADD8E6 url('../../Content/images/help.png') no-repeat;
background-position: left top;
}
Additional ViewData in Action
To see the additional ViewData working debug /Employee:
How does this work?
This is very simple in our Index view the call to:
<%: Html.EditorFor(model => model.Offices, new { HelpText="To select multiple offices press 'Ctrl' and select the option" }) %>
Adds the anonymous object to the ViewData dictionary, using the property name as the key and the property value as the value.
In the Offices template we get the value from the ViewData dictionary in the normal way:
<%var helpText = ViewData["HelpText"] ?? string.Empty;%>
Lastly, we just display the text in the template:
<div class="editor-help"><%:helpText%></div>
Conclusion
Understanding the template helper's additional ViewData overload should help you avoid those view hacks that we all do from time to time. When using this be careful not to break separation of concerns, for example passing some information to configure the display of a view is correct use of this functionality, creating a new model in a view and passing it to a template breaks SoC and should be avoided.
The example code in this post and download use .NET 4.0 and Visual Studio 2010. If you would like me to add a .NET 3.5/Visual Studio 2008 download post a comment.