Wednesday, 28 September 2011

View Templates in MVC 3

Somebody asked me a question today regarding display templates in MVC, the questioner had a model that looked like this -

public class Team
{
     public int TeamId { get; set; }
     [Required]
     public string Name { get; set; }
     public string City { get; set; }
     public DateTime Founded { get; set; }
}

and they wanted to set up a custom display template for the 'City' property of the 'Team' object. Ignoring the fact that this was a bit of an odd request and the questioners model data seemed to have been stolen from Steve Sanderson's MVC Scaffolding posts I asked to look at his project structure, this is what I saw -




In many ways, this looks good, a 'DisplayTmeplates' folder has been created in the 'Teams' view folder and a template has been added to the folder. If we take a look at the code that displays the 'City' display name it also looks good -

@Html.DisplayFor(m => m.City)

N.B. It's always worth double checking that the rendering code for the data you want to add to the template is using a method that will hook in to the templates, if you have code similar to this the templates will always be ignored -

Name
@Model.Name

So, what has gone wrong? The problem lies in the name of the template file. When rendering a display template, MVC will render the default template unless it finds a template that overrides the default template in either the 'shared/dispalytemplates' folder or the 'dispalytemplates' folder of the current controller. The key thing to remember though is that MVC attempts to find the template based on the type of the data being rendered not on the name of the property. So if we wanted to get the template to show for the example above we'd need to add a template file to one of the following locations -

  • Views\Shared\DisplayTemplates\String.cshtml
  • Views\Teams\DisplayTemplates\String.cshtml

The downside of this approach is it will render this template for all strings in the first instance and for all properties of type string on the 'Team' model class in the second instance. This could cause problems.

If you did want to get the city template to apply only to the city property there are a couple of options -

Specifiy the template in the 'DisplayFor' call -

@Html.DisplayFor(m => m.City,"City")

Specifiy a UI hint for the property in the model -

[UIHint("City")]
public string City { get; set; }

Finally here's an example of what the template file might have conatained -

@model System.String
@ViewData.ModelMetadata.GetDisplayName() -  
          @Model (This is special text inclued in the template)

Which would render -




If you need further information regarding the 'ModelMetdata' calls that are made in the view code you can find it here in Brad Wilson's excellent set of blog posts exploring the templating system in MVC.

1 comment: