Wednesday, February 1, 2012

ASP.NET MVC - LabelFor htmlAttributes Extension

Part of using any new framework such as ASP.NET MVC, is finding out what it can and cannot do. I was designing the layout for my application's web forms and found out the LabelFor HTML helper didn't allow one to pass html attributes like many of the other HTML helpers. Digging around on the web, I found this handy bit of code on Imran Baloch's blog that neatly solved my problem.

I have re-posted it here for convenience.

LabelExtensions.cs (comments removed for clarity):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Linq.Expressions;

namespace CritterHealthRecords.Web.Helpers
{
    public static class LabelExtensions
    {
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
        {
            return LabelFor(html, expression, new RouteValueDictionary(htmlAttributes));
        }

        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();

            if (String.IsNullOrEmpty(labelText))
            {
                return MvcHtmlString.Empty;
            }

            TagBuilder tag = new TagBuilder("label");
            tag.MergeAttributes(htmlAttributes);
            tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
            tag.SetInnerText(labelText);

            return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
        }
    }
}

Using the extension is easy:


<%=Html.LabelFor(m => m.UserName, new { @class="field-label"})%>

Which renders the following HTML:


<label class="field-label" for="UserName">User Name</label>