2

I have a project in ASP.NET MVC 4 wich recently has been visually improved with the use of Bootstrap.

Although I'm pretty pleased with it, there is one think I'm not very satisfied with.

I've had to add

new { @class = "form-control input-sm" } 

To all my Html.TextBox helper calls. And I was wondering if there is a more graceful way to do this... maybe overriding the default TextBox helper?

Any insight in this will be much appreciated.

Siguza
  • 21,155
  • 6
  • 52
  • 89
Bardo
  • 2,470
  • 2
  • 24
  • 42

2 Answers2

5

Edit

Its actually doable - to override extension method. For reference see: How to override an existing extension method.

Views are created in "ASP" namespace; so in order for your extension method to override default extension methods in System.Web.Mvc.Html (namely static class InputExtensions) you have to declare your overrides in "ASP" namespace as well. So in your MVC project define class InputExtensionOverride.cs like this:

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

namespace ASP {
  public static class InputExtensionsOverride {
    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
      return InputExtensionsOverride.TextBoxFor<TModel, TProperty>(htmlHelper, expression, (string)null);
    }

    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) {
      return InputExtensionsOverride.TextBoxFor<TModel, TProperty>(htmlHelper, expression, (string)null, htmlAttributes);
    }

    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) {
      var dictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
      return InputExtensionsOverride.TextBoxFor<TModel, TProperty>(htmlHelper, expression, (string)null, dictionary);
    }

    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string format) {
      return InputExtensionsOverride.TextBoxFor<TModel, TProperty>(htmlHelper, expression, format, ((IDictionary<string, object>)null));
    }

    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string format, IDictionary<string, object> htmlAttributes) {
      htmlAttributes = SetCommonAttributes<TModel, TProperty>(htmlHelper, expression, ref htmlAttributes);
      return System.Web.Mvc.Html.InputExtensions.TextBoxFor(htmlHelper, expression, format, htmlAttributes);
    }

    private static IDictionary<string, object> SetCommonAttributes<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, ref IDictionary<string, object> htmlAttributes) {
      if (htmlHelper == null) {
        throw new ArgumentNullException("htmlHelper");
      }

      if (expression == null) {
        throw new ArgumentNullException("expression");
      }

      if (htmlAttributes == null) {
        htmlAttributes = new Dictionary<string, object>();
      }

      if (!htmlAttributes.ContainsKey("class")) {
        htmlAttributes.Add("class", "form-control input-sm");
      }

      return htmlAttributes;
    }
  }

}

In your view there is no change - i.e.:

@using (Html.BeginForm()) {
   @Html.TextBoxFor(m => m.SomeProperty)
}

will generate text box with your "form-control input-sm" css class.

Community
  • 1
  • 1
Ondrej Svejdar
  • 21,349
  • 5
  • 54
  • 89
  • This could be a good option, however I'd like to extend the solution to all our projects and I'd like to make it as transparent as it's possible to the rest of our developers and, of course, try to simplify their work. So, is there a way in wich I could change actual TextBox's method behaviour? If I can do so I just have to replicate de change into the rest of the projects and into our generic framework and all will update without having to change more code. – Bardo Apr 25 '14 at 07:23
  • That's exactly what I was looking for. I've tested it and works perfectly. Great solution for what I think could be a common problem. – Bardo Apr 28 '14 at 07:58
  • Emphasis on the ASP namespace. Works great in MVC5 for adding Bootstrap 3 classes. Thank you! – Matty Bear Jun 30 '15 at 11:28
3

I think you should simply add class for input field using jquery.

$('input[type=text]').addClass('form-control input-sm');

Adding custom helper class for textbox won't be helpfull since you have to change code again in many plcaces.

As example, if this is your custom helper textboxfor

public static class MyHtmlHelpers
{
    public static MvcHtmlString MyTextBoxFor<TModel, TProperty>(
         this HtmlHelper<TModel> helper, 
         Expression<Func<TModel, TProperty>> expression)
    {
        return helper.TextBoxFor(expression, new { @class = "form-control input-sm" }) 
    }
}

You'll have to convert TextBoxFor into this MyTextBoxFor

@Html.MyTextBoxFor(model => model.FirstName) 
Ashwini Verma
  • 7,477
  • 6
  • 36
  • 56
  • That's exactly why I was wondering about overriding the actual method. I like de idea of use Bootstrap in all of our projects, and I'd like it to be transparent for the rest of the developers, so if I change the behaviour of TextBox method on our generic project they will only see that now our inputs are more "user friendly". Your solution is a good idea and seems fair enough, however I'll dig a bit into my first option before accepting it. – Bardo Apr 25 '14 at 07:19
  • 1
    It seems that there's no way to override an Html Helper maintaining the same signature without completely rebuilding the whole library on a different namespace, so your solution is the most clean. Not the one I'd like to apply, but a valid a hassle free one for sure! – Bardo Apr 25 '14 at 08:27
  • I've changed the answer to Ondrej's one after it's edit because he finally hit the exactly spot of what I was looking for. Your answer is also valid (and involves less code), however I think his answer is cleaner. I'd like to approve both as valid answers :( – Bardo Apr 28 '14 at 08:00