70

I have a simple field form

<div class="field fade-label">
    @Html.LabelFor(model => model.Register.UserName)
    @Html.TextBoxFor(model => model.Register.UserName)
</div>

and this results in:

<div class="field fade-label">
    <label for="Register_UserName">Username (used to identify all services, from 4 to 30 chars)</label>
    <input type="text" value="" name="Register.UserName" id="Register_UserName">
</div>

but I want that LabelFor code append a <span> inside so I can end up having:

<label for="Register_UserName">
    <span>Username (used to identify all services, from 4 to 30 chars)</span>
</label>

How can I do this?

All examples use EditorTemplates but this is a LabelFor.

balexandre
  • 73,608
  • 45
  • 233
  • 342
  • This will cause an ambigous call exception since the signature is identical to the existing extension method. There is no overriding extension methods. – Nilzor Mar 08 '11 at 16:00
  • @Nilzor, there is no extension with such parameters, you're safe to use the code in my answer, remember, it's `LabelFor` not `EditorFor`. – balexandre Mar 08 '11 at 19:41
  • Yes, you're right. What I should have said is that your methods does not override the @Html.LabelFor(model => model.Register.UserName) construct. If you try to add an overload with this signature, you will get an ambigous call exception, as I've tested. Your solution is sound, but it requires you to change the invoking code (the views). – Nilzor Mar 18 '11 at 09:41
  • @balexandre How do you do to override the "normal" LabelFor method? – Romias Nov 26 '11 at 22:35
  • Please move your edited solution into an actual answer below. – BoltClock Feb 27 '12 at 17:06

3 Answers3

72

You'd do this by creating your own HTML helper.

http://www.asp.net/mvc/tutorials/creating-custom-html-helpers-cs

You can view the code for LabelFor<> by downloading the source for ASP.Net MVC and modify that as a custom helper.


Answer added by balexandre

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));

        TagBuilder span = new TagBuilder("span");
        span.SetInnerText(labelText);

        // assign <span> to <label> inner html
        tag.InnerHtml = span.ToString(TagRenderMode.Normal);

        return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
    }
}
Community
  • 1
  • 1
Bryan S.
  • 1,434
  • 11
  • 8
5

I expanded upon balealexandre's answer and added in the ability to specify HTML to include both before and after your label text. I added a bunch of method overloads and comments. I hope this helps folks!

Also snagged information from here: Html inside label using Html helper

namespace System.Web.Mvc.Html
{
    public static class LabelExtensions
    {
        /// <summary>Creates a Label with custom Html before the label text.  Only starting Html is provided.</summary>
        /// <param name="startHtml">Html to preempt the label text.</param>
        /// <returns>MVC Html for the Label</returns>
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Func<object, HelperResult> startHtml)
        {
            return LabelFor(html, expression, startHtml, null, new RouteValueDictionary("new {}"));
        }

        /// <summary>Creates a Label with custom Html before the label text.  Starting Html and a single Html attribute is provided.</summary>
        /// <param name="startHtml">Html to preempt the label text.</param>
        /// <param name="htmlAttributes">A single Html attribute to include.</param>
        /// <returns>MVC Html for the Label</returns>
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Func<object, HelperResult> startHtml, object htmlAttributes)
        {
            return LabelFor(html, expression, startHtml, null, new RouteValueDictionary(htmlAttributes));
        }

        /// <summary>Creates a Label with custom Html before the label text.  Starting Html and a collection of Html attributes are provided.</summary>
        /// <param name="startHtml">Html to preempt the label text.</param>
        /// <param name="htmlAttributes">A collection of Html attributes to include.</param>
        /// <returns>MVC Html for the Label</returns>
        public static MvcHtmlString LabelFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, Func<object, HelperResult> startHtml, IDictionary<string, object> htmlAttributes)
        {
            return LabelFor(html, expression, startHtml, null, htmlAttributes);
        }

        /// <summary>Creates a Label with custom Html before and after the label text.  Starting Html and ending Html are provided.</summary>
        /// <param name="startHtml">Html to preempt the label text.</param>
        /// <param name="endHtml">Html to follow the label text.</param>
        /// <returns>MVC Html for the Label</returns>
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Func<object, HelperResult> startHtml, Func<object, HelperResult> endHtml)
        {
            return LabelFor(html, expression, startHtml, endHtml, new RouteValueDictionary("new {}"));
        }

        /// <summary>Creates a Label with custom Html before and after the label text.  Starting Html, ending Html, and a single Html attribute are provided.</summary>
        /// <param name="startHtml">Html to preempt the label text.</param>
        /// <param name="endHtml">Html to follow the label text.</param>
        /// <param name="htmlAttributes">A single Html attribute to include.</param>
        /// <returns>MVC Html for the Label</returns>
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Func<object, HelperResult> startHtml, Func<object, HelperResult> endHtml, object htmlAttributes)
        {
            return LabelFor(html, expression, startHtml, endHtml, new RouteValueDictionary(htmlAttributes));
        }

        /// <summary>Creates a Label with custom Html before and after the label text.  Starting Html, ending Html, and a collection of Html attributes are provided.</summary>
        /// <param name="startHtml">Html to preempt the label text.</param>
        /// <param name="endHtml">Html to follow the label text.</param>
        /// <param name="htmlAttributes">A collection of Html attributes to include.</param>
        /// <returns>MVC Html for the Label</returns>
        public static MvcHtmlString LabelFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, Func<object, HelperResult> startHtml, Func<object, HelperResult> endHtml, IDictionary<string, object> htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            string htmlFieldName = ExpressionHelper.GetExpressionText(expression);

            //Use the DisplayName or PropertyName for the metadata if available.  Otherwise default to the htmlFieldName provided by the user.
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
            if (String.IsNullOrEmpty(labelText))
            {
                return MvcHtmlString.Empty;
            }

            //Create the new label.
            TagBuilder tag = new TagBuilder("label");

            //Add the specified Html attributes
            tag.MergeAttributes(htmlAttributes);

            //Specify what property the label is tied to.
            tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));

            //Run through the various iterations of null starting or ending Html text.
            if (startHtml == null && endHtml == null) tag.InnerHtml = labelText;
            else if (startHtml != null && endHtml == null) tag.InnerHtml = string.Format("{0}{1}", startHtml(null).ToHtmlString(), labelText);
            else if (startHtml == null && endHtml != null) tag.InnerHtml = string.Format("{0}{1}", labelText, endHtml(null).ToHtmlString());
            else tag.InnerHtml = string.Format("{0}{1}{2}", startHtml(null).ToHtmlString(), labelText, endHtml(null).ToHtmlString());

            return MvcHtmlString.Create(tag.ToString());
        }
    }
}
Community
  • 1
  • 1
Setarcos
  • 81
  • 1
  • 1
3

LabelFor is an extension method (static) and therefore cannot be overridden. You'd need to create your own Html Helper Extension method to achieve what you require.

Darren Lewis
  • 8,338
  • 3
  • 35
  • 55
  • 2
    Editor templates CAN be "overriden", although they TOO are static methods. – Linkgoron Mar 04 '11 at 16:34
  • 1
    We're not talking about Editor templates here. An Editor template is a partial view discovered through convention. It has nothing to do with overrides or static declaration of an extension method. – Darren Lewis Mar 04 '11 at 19:23
  • 12
    Yes, but when someone sees LabelFor, and then sees that it's similar to EditorFor he MIGHT think that it, too, can be overiden by convention. That's exactly what the OP asked. This has nothing to do with method overloading and static methods. – Linkgoron Mar 04 '11 at 19:31