ASP.NET MVC Tip #14 – Create a Template Helper Method
In this tip, you learn how to create and use templates in the MVC framework that you can use to display database data. I show you how to create a new MVC Helper method named the RenderTemplate() method.
While I was back home in California during the 4th of July weekend, I was talking to my smarter, older brother about the differences among building web applications with ASP.NET Web Forms, ASP.NET MVC, and Ruby on Rails. I was bemoaning the fact that I really missed using controls when building an ASP.NET MVC application. In particular, I was complaining that I missed the clean separation between HTML and UI logic provided by the templates that you get with an ASP.NET Web Forms control. A Repeater control is really not the same as a for…next loop.
My brother told me something surprising. He said “Templates, Ruby on Rails has templates, they are called partials.” At first, I didn’t understand. I thought partials in the Ruby on Rails world were more or less the same as user controls in the ASP.NET MVC world. However, my brother explained that when you render a partial in a Ruby on Rails application, you can pass a collection of items. Each item in the collection is rendered with the partial.
Cool. You can use the same approach to create templates in an ASP.NET MVC application. Create a new helper method that accepts an IEnumerable and a path to a user control. The helper method can use the user control as a template for each item from the IEnumerable. Listing 1 contains the code for a new helper method named the RenderTemplate() method.
Listing 1 – TemplateExtensions.vb (VB.NET)
Imports System
Imports System.Text
Imports System.Collections
Imports System.Web.Mvc
Public Module TemplateExtensions
<System.Runtime.CompilerServices.Extension()> _
Public Function RenderTemplate(ByVal helper As HtmlHelper, ByVal items As IEnumerable, ByVal virtualPath As String) As String
Dim sb = New StringBuilder()
For Each item As Object In items
sb.Append(helper.RenderUserControl(virtualPath, item))
Next item
Return sb.ToString()
End Function
End Module
Listing 1 – TemplateExtensions.cs (C#)
using System;
using System.Text;
using System.Collections;
using System.Web.Mvc;
namespace Helpers
{
public static class TemplateExtensions
{
public static string RenderTemplate(this HtmlHelper helper, IEnumerable items, string virtualPath)
{
var sb = new StringBuilder();
foreach (object item in items)
{
sb.Append( helper.RenderUserControl(virtualPath, item));
}
return sb.ToString();
}
}
}
Imagine, for example, that you want to display a list of movies. You can use the HomeController in Listing 2 to return a collection of Movie entities. The Index() action executes a LINQ to SQL query and passes the query results to the Index view.
Listing 2 – HomeController.vb (VB.NET)
Public Class HomeController
Inherits System.Web.Mvc.Controller
Private _dataContext As New MovieDataContext()
Public Function Index() As ActionResult
Dim movies = _dataContext.Movies
Return View(movies)
End Function
End Class
Listing 2 – HomeController.cs (C#)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Tip14.Models;
namespace Tip14.Controllers
{
public class HomeController : Controller
{
private MovieDataContext _dataContext = new MovieDataContext();
public ActionResult Index()
{
var movies = _dataContext.Movies;
return View(movies);
}
}
}
The view in Listing 3 simply calls the RenderTemplate() method passing the method the ViewData.Model and the path to an MVC user control that contains the template for each movie.
Listing 3 -- Index.aspx (VB.NET)
<%@ Page Language="VB" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="Tip14.Index" %>
<%@ Import Namespace="Tip14" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<div>
<%= Html.RenderTemplate(ViewData.Model, "~/Views/Home/MovieTemplate.ascx") %>
</div>
</body>
</html>
Listing 3 -- Index.aspx (C#)
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="Tip14.Views.Home.Index" %>
<%@ Import Namespace="Helpers" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<div>
<%= Html.RenderTemplate(ViewData.Model, "~/Views/Home/MovieTemplate.ascx") %>
</div>
</body>
</html>
The MovieTemplate.ascx MVC User Control is strongly typed. The code-behind file for this user control is contained in Listing 4. Notice that the User Control is strongly typed to represent a Movie entity.
Listing 4 – MovieTemplate.ascx.vb (VB.NET)
Public Partial Class MovieTemplate
Inherits System.Web.Mvc.ViewUserControl(Of Movie)
End Class
Listing 4 – MovieTemplate.ascx.cs (C#)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Tip14.Models;
namespace Tip14.Views.Home
{
public partial class MovieTemplate : System.Web.Mvc.ViewUserControl<Movie>
{
}
}
Finally, the view part of the MVC user control is contained in Listing 5. Notice that you can use expressions like ViewData.Model.Title and ViewData.Model.Director to display the movie title and movie director. These expressions work because you are using a strongly typed MVC User Control that represents a movie entity.
Listing 5 – MovieTemplate.ascx (VB.NET)
<%@ Control Language="VB" AutoEventWireup="false" CodeBehind="MovieTemplate.ascx.vb" Inherits="Tip14.MovieTemplate" %>
<b><%= ViewData.Model.Title %></b>
<br />
Director: <%= ViewData.Model.Director %>
<hr />
Listing 5 – MovieTemplate.ascx (C#)
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MovieTemplate.ascx.cs" Inherits="Tip14.Views.Home.MovieTemplate" %>
<b><%= ViewData.Model.Title %></b>
<br />
Director: <%= ViewData.Model.Director %>
<hr />
When you request the Index view, you get the page shown in Figure 1. Notice that the MVC User Control has been rendered for each movie.
Figure 1 – The Movies records rendered with a template
Summary
In this tip, I explained how you can create and use templates in an ASP.NET MVC application. I’ve demonstrated how you can create a template by creating an MVC User Control and use the template to control how a set of database records is rendered. Now, there is no reason to ever use a Repeater control in an ASP.NET MVC application.
You can download the TemplateExtensions (which includes the RenderTemplate() method) by clicking the following link: