Utilize ASP.NET MVC's display templates to customise how EPiServer's PropertyFor method renders properties.
When building a site with EPiServer CMS 7 and ASP.NET MVC the standard way of rendering a property is to use the PropertyFor helper method. Here’s an example of a simple view that renders a XhtmlString property and a LinkItemCollection property.
@model Pigeon.Models.Pages.StandardPage @Html.PropertyFor(x => x.MainBody) @Html.PropertyFor(x => x.Links)
In both of the above cases the PropertyFor method will eventually call the ASP.NET MVC helper method DisplayFor to render the properties. The DisplayFor method uses conventions to locate a partial view depending of the type of object being rendered. However, in an empty EPiServer 7 MVC site no such partial view exists for XhtmlString, LinkItemCollection or any other built in property type. Yet, when viewing a page using the above view they are displayed.
Clearly, something is written to the output. Inspecting the elements we see that the MainBody XhtmlString is outputted as is and the LinkItemCollection is rendered as an ul/li list.
Although no template for built in properties exists in our project the PropertyFor and DisplayFor methods can render them anyway. That’s nice, but what’s going on here? In fact a display template for built in property types such as LinkItemCollection and XhtmlString does exist on our site, just not in our project.
Default display templates for built in property types
While the ASP.NET MVC infrastructure by convention looks in /Views/Shared/DisplayTemplates to find display templates EPiServer has registered another path for resolving views, [CMS_Installation_Directory]/Application/Util/Views/. Looking in this folder we’ll find display templates for a bunch of property types:
Opening LinkItemCollection.ascx reveals this:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<EPiServer.SpecializedProperties.LinkItemCollection>" %> <%@ Import Namespace="EPiServer.Web.Mvc.Html" %> <ul> <% foreach(var linkItem in Model ?? Enumerable.Empty<EPiServer.SpecializedProperties.LinkItem>()) %> <% { %> <li> <%: Html.PageLink(linkItem)%> </li> <% } %> </ul>
What if we want to change how a certain type of property is rendered? Technically we could make changes in these default display templates, but that would effect all sites on the same computer and would break after an EPiServer upgrade. In other words – don’t do that.
Using custom display templates for built in property types
If we want to change how a property type is rendered using the PropertyFor or DisplayFor methods we simply create a display template with the name of the type which we put in the folder ASP.NET MVC will look for by convention, by default ~/Views/Shared/DisplayTemplates. For instance, to change the rendering of LinkItemCollection we can create a view named LinkItemCollection.
By putting this in the “local” display templates folder this view will take priority and will be used instead of EPiServer’s default one. In our own display template we’re free to render LinkItemCollections however we want. We could for instance render them using ol lists instead of ul lists:
@using EPiServer.SpecializedProperties @model LinkItemCollection <ol> @foreach(var linkItem in Model ?? Enumerable.Empty<LinkItem>()) { <li>@Html.PageLink(linkItem)</li> } </ol>
Passing arguments to display templates
In addition to being able to completely change the default rendering of properties using custom display templates we can also reap another, perhaps more useful, benefits from creating them. Looking at the default templates in EPiServer’s installation directory we can see that they aren’t very customizable. For instance, let’s say we’re happy with rendering LinkItemCollections using ul/li lists but in some places we would like to pass a CSS class or two to the PropertyFor method which should be added to the list. That is, we’d like to do something like this:
@Html.PropertyFor(x => x.Links, new { @class="nav nav-tabs"})
Without customizing how LinkItemCollections are rendered passing the additional view data like above won’t have any effect. But we can change that using our own display template.
@using EPiServer.SpecializedProperties @model LinkItemCollection <ul class="@ViewData["class"]"> @foreach(var linkItem in Model ?? Enumerable.Empty<LinkItem>()) { <li class="@ViewData["itemClass"]">@Html.PageLink(linkItem)</li> } </ul>
In the above display template for LinkItemCollections we look for a “class” value in the view data which we add to the ul element. We also look for a “itemClass” value which we add to each li element. Now passing a CSS class in the additionalViewData argument to the PropertyFor method has an effect.
PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.
My book
Want a structured way to learn EPiServer 7 development? Check out my book on Leanpub!
Comments
comments powered by Disqus