We have a rule in the project I'm working on now that it must work on desktop, tablet, and mobile, so we are implementing Responsive Design using the Foundation framework. Unfortunately ASP.NET MVC doesn't follow one of the guidelines that our design group set out for us, so we built our own extensions to enforce said guidelines.

The Problem

Our design team has mandated that any input control must be wrapped in a label tag so that when you click on the text, it behaves like you clicked on the control.

Which leads to a lot of this:

<label>  
    @Html.DisplayNameFor(x => x.RegularText)
    @Html.TextBoxFor(x => x.RegularText)
</label>  

The Solution

It occurred to a few of us that we could just write a an extender to use this, or at least a few extenders, one for each input.

Which, on the face of it, is not difficult. But it sent me down a rabbit hole of learning about how we deal with expressions and metadata in ASP.NET and MVC.

Anyway, here's the extension:

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

    TagBuilder builder = new TagBuilder("label");
    builder.InnerHtml = labelText + html.TextBoxFor(expression, htmlAttributes).ToString();
    return MvcHtmlString.Create(builder.ToString());
}

Down the Rabbit Hole

Having never build an extension of the ControlFor class, it took me awhile to wrap my head around each of these components. But, when I got it, I got it, and now it makes total sense.

The Expression<> of our Love

The first thing was the Expression<> class, which to me still seems like a little bit of magic. The way it was explained to me is that it represents a LINQ expression itself, e.g. x => x.Property, rather than the actual value of Property. Which essentially means we want to look inside the actual expression itself rather than value it.

(For a little more explanation on Expression<> and especially why we want 'Expression>' rather than just Func<>, check out this wonderful StackOverflow answer.)

Big Metadata

But we need Expression<> because without it we can't get the next piece. ModelMetadata stores any metadata about the model in use. It's really just a container for the ModelMetadataProvider and ModelValidator classes, but it gives a nice common access point for both of those.

The ModelMetadata contains information we place in Attributes, so we need it to get the Display Name we want to display in the wrapped control. We can even do that by passing an Expression<> to the ModelMetadata class:

ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);  

(Display) Name Your Price

Speaking of the Display Name, we need to handle the situation where we don't have one. We also need to handle a less common situation; if we are using this extender without also using a ViewModel.

string fieldName = ExpressionHelper.GetExpressionText(expression);  
string labelText = metadata.DisplayName ?? metadata.PropertyName ?? fieldName.Split('.').Last();  

The Expression Text is this: x => x.Name as a string. So, we need just what comes after the '.'

Building Trust (and an Label)

The final part of all this is to wrap everything in a <label>.

TagBuilder builder = new TagBuilder("label");  
builder.InnerHtml = labelText + html.TextBoxFor(expression,        htmlAttributes).ToString();  
return MvcHtmlString.Create(builder.ToString());  

And now, on the view, we can just do this:

@Html.WrappedTextBoxFor(x => x.Name)

And we're done!

Download the Code

Feel free to grab a sample solution for this from GitHub and let me know in the comments if this helped you out!

Happy Coding!