Subscribe to this Blog

Subscribe to this Blog

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

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

Published Friday, June 13, 2008 1:07 PM by swalther
Filed under: ,

Comments

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

Thursday, June 26, 2008 11:29 AM by David Kemp

Why have you used

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

rather than

public static string UnorderedList<ITEMTYPE>(this HtmlHelper helper, IEnumerable<ITEMTYPE> 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<T> being passed into the method.

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

Friday, June 27, 2008 11:22 AM by swalther

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

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

Thursday, July 31, 2008 5:21 AM by David

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!

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

Sunday, August 24, 2008 9:24 AM by Frank

Great! Very helpful!

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

Thursday, November 13, 2008 8:26 PM by cjefferies

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.