ASP.NET MVC Tip #1 - Create New HTML Helpers with Extension Methods

In this tip, I show you how you can create two new HTML Helpers that you can use within an ASP.NET MVC View. I show you how you can use extension methods to create new HTML Helpers for displaying bulleted and numbered lists.

When building a View for an ASP.NET MVC application, you can take advantage of HTML Helpers to render standard HTML tags. For example, instead of typing this:

<input name="inpSubmit" type="submit" value="Click Here!" />

You can type this:

<%= Html.SubmitButton("inpSubmit", "Click Here!") %>

Over the long run, HTML Helpers can save you a lot time. But what if there isn’t an HTML Helper for a tag that you want to render? For example, imagine that you want to display a bulleted list of database records in a View. The HtmlHelper class doesn’t include a method that lets you render a bulleted list. Don’t give up. If the HTML Helper doesn't include a method that you need, just extend it!

You can add new functionality to the HtmlHelper class by creating new extension methods. An extension method looks just like a normal instance method. However, unlike a normal instance method, you add extension methods to a class by defining the methods in a completely different class.

In Visual Basic .NET, you create extension methods by creating a module and decorating the extension methods with a special attribute. In C#, you define extension methods in a static class and use the keyword this to indicate the class being extended.

Here’s how you can add extension methods to the HtmlHelper class to display both ordered and unordered list of database records:

Listing 1 – ListExtensions.vb (VB.NET)

   1: Imports System
   2: Imports System.Collections
   3: Imports System.Text
   4: Imports System.Web
   5: Imports System.Web.Mvc
   6: Imports System.Runtime.CompilerServices
   7:  
   8:  
   9: Namespace HtmlHelpers
  10:  
  11:     Public Module ListExtensions
  12:  
  13:         <Extension()> _
  14:         Public Function OrderedList(ByVal HtmlHelper As HtmlHelper, ByVal items As Object) As String
  15:             Return "<ol>" + ListExtensions.GetListItems(items) + "</ol>"
  16:         End Function
  17:  
  18:         <Extension()> _
  19:         Public Function UnorderedList(ByVal HtmlHelper As HtmlHelper, ByVal items As Object) As String
  20:             Return "<ul>" + ListExtensions.GetListItems(items) + "</ul>"
  21:         End Function
  22:  
  23:  
  24:         Private Function GetListItems(ByVal items As Object) As String
  25:             If items Is Nothing Then
  26:                 Throw New ArgumentNullException("items")
  27:             End If
  28:             If Not TypeOf items Is IEnumerable Then
  29:                 Throw New InvalidCastException("items must be IEnumerable")
  30:             End If
  31:  
  32:             Dim EnumItems As IEnumerable = CType(items, IEnumerable)
  33:             Dim builder As New StringBuilder()
  34:             For Each item As Object In EnumItems
  35:                 builder.AppendFormat("<li>{0}</li>", HttpUtility.HtmlEncode(item.ToString()))
  36:             Next
  37:             Return builder.ToString()
  38:         End Function
  39:  
  40:     End Module
  41: End Namespace

Listing 1 – ListExtensions.cs (C#)

   1: using System;
   2: using System.Collections;
   3: using System.Text;
   4: using System.Web;
   5: using System.Web.Mvc;
   6:  
   7: namespace BulletedListHelper.HtmlHelpers
   8: {
   9:     public static class ListExtensions
  10:     {
  11:         public static string OrderedList(this HtmlHelper helper, Object items)
  12:         {
  13:             return "<ol>" + ListExtensions.GetListItems(items) + "</ol>";
  14:         }
  15:  
  16:         public static string UnorderedList(this HtmlHelper helper, Object items)
  17:         {
  18:             return "<ul>" + ListExtensions.GetListItems(items) + "</ul>";
  19:         }
  20:  
  21:  
  22:         private static string GetListItems(Object items)
  23:         {
  24:             if (items == null)
  25:                 throw new ArgumentNullException("items");
  26:             if (items is IEnumerable == false)
  27:                 throw new InvalidCastException("items must be IEnumerable");
  28:  
  29:             var enumItems = (IEnumerable)items;
  30:             var builder = new StringBuilder();
  31:             foreach (Object item in enumItems)
  32:                 builder.AppendFormat("<li>{0}</li>", HttpUtility.HtmlEncode(item.ToString()));
  33:             return builder.ToString();
  34:         }
  35:  
  36:     }
  37: }

The ListExtensions class has two public methods: OrderedList() and UnorderedList(). You pass a collection of items to either method to display either a numbered or bulleted list of items. Notice that these methods return strings. Really, an HTML Helper method is nothing more than a method that renders a formatted string to the browser.

After you create the extension methods, you can use the methods in a View like this:

Listing 2 – Index.aspx

   1: <%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="BulletedListHelper.Index" %>
   2: <%@ Import Namespace="BulletedListHelper.HtmlHelpers" %>
   3:  
   4: <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
   5:   
   6:  
   7:    <h1>Movies (Ordered)</h1>
   8:  
   9:    <%= Html.OrderedList(ViewData.Model) %>
  10:  
  11:  
  12:    <h1>Movies (Unordered)</h1>
  13:  
  14:  
  15:    <%= Html.UnorderedList(ViewData.Model) %>
  16:  
  17:  
  18: </asp:Content>

Notice that the BulletedList.HtmlHelpers namespace gets imported at the top of the file. The method Html.OrderedList() is used to render a numbered list and the method Html.UnorderedList() is used to render a bulleted list. Notice that these methods are being called on the HtmlHelper exposed by the Html property of the View just like any other extension method. When you open this View in a browser, you get the page in Figure 1:

Figure 1 – Index.aspx Rendered with Custom HTML Helpers

clip_image002

Finally, the Index() method exposed by the HomeController in Listing 3 illustrates how you can pass a collection of movie records to the Index.aspx View. The movie records are retrieved by taking advantage of a Linq to SQL query.

Listing 3 – HomeController.vb (VB.NET)

   1: Public Class HomeController
   2:     Inherits System.Web.Mvc.Controller
   3:  
   4:     Private db As New MoviesDataContext()
   5:  
   6:     Function Index()
   7:         Dim movies = From m In db.Movies Select m.Title
   8:         Return View(movies)
   9:     End Function
  10:  
  11:    
  12: End Class

Listing 3 – HomeController.cs (C#)

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Web;
   5: using System.Web.Mvc;
   6: using BulletedListHelper.Models;
   7:  
   8: namespace BulletedListHelper.Controllers
   9: {
  10:     public class HomeController : Controller
  11:     {
  12:  
  13:         private MoviesDataContext db = new MoviesDataContext();  
  14:  
  15:         public ActionResult Index()
  16:         {
  17:             var movies = from m in db.Movies select m.Title;
  18:             return View(movies);
  19:         }
  20:  
  21:     }
  22: }

You can use this approach to render just about anything within an ASP.NET MVC View. For example, you can use a similar approach to create TreeViews, Menus, tabstrips, whatever.

Click Here to Download the Source Code

5 Comments

  • Why have you used

    public static string UnorderedList(this HtmlHelper helper, object items)

    rather than

    public static string UnorderedList(this HtmlHelper helper, IEnumerable items) ?

    Surely your method catches the wrong type in the test/at run time, whereas using generics, we can stop anything that doesn't implement IEnumerable being passed into the method.


  • @David - Good point, I should have used generics here instead of making an assumption about the type and casting.

  • Hi, have you had any luck using ToAttributeList() in your extension methods in vb.net? I always get an error saying ToAttributeList is not a member of VB$AnonymousType_0(Of String) and i have no idea why...

    Cheers!

  • Great! Very helpful!

  • very helpful steve, thanks. I have been watching/reading your tutorials to learn MVC and you do a good job at laying out the basics of the MVC framework.

Comments have been disabled for this content.