Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASPNET MVC - Override Html.TextBoxFor(model.property) with a new helper with same signature?

I want to override Html.TextBoxFor() with my own helper that has the exact same signature (but a different namespace of course) - is this possible, and if so, how?

The reason for this is that I have 100+ views in an already existing app, and I want to change the behaviour of TextBoxFor so that it outputs a maxLength=n attribute if the property has a [StringLength(n)] annotation.

The code for automatically outputting maxlength=n is in this question: maxlength attribute of a text box from the DataAnnotations StringLength in Asp.Net MVC. But my question is not a duplicate - I am trying creating a more generic solution: where the DataAnnotaion flows into the html automatically without any need for additional code by the person writing the view.

In the referenced question, you have to change every single Html.TexBoxFor to a Html.CustomTextBoxFor. I need to do it so that the existing TextBoxFor()'s do not need to be changed - hence creating a helper with the same signature: change the behaviour of the helper method, and all existing instances will just work without any changes (100+ views, at least 500 TextBoxFor()s - don't want to manually edit that).

I tried this code: (And I need to repeat it for each overload of TextBoxFor, but once the root problem is solved, that will be trivial)

namespace My.Helpers
{
    public static class CustomTextBoxHelper
    {
        public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes, bool includeLengthIfAnnotated)
        {
                // implementation here
        }
    }
}

But I am getting a compiler error in the view on Html.TextBoxFor(): "The call is ambiguous between the following methods or properties" (of course). Is there any way to do this?

Is there an alternative approach that would allow me to change the behaviour of Html.TextBoxFor, so that the views that already use it do not need to be changed?

like image 319
JK. Avatar asked May 05 '10 01:05

JK.


People also ask

How disable HTML TextboxFor in MVC?

You can make the input 'read only' by using 'readonly'. This will let the data be POSTED back, but the user cannot edit the information in the traditional fashion. Keep in mind that people can use a tool like Firebug or Dragonfly to edit the data and post it back.

What is difference between HTML textbox and HTML TextboxFor?

IMO the main difference is that Textbox is not strongly typed. TextboxFor take a lambda as a parameter that tell the helper the with element of the model to use in a typed view. You can do the same things with both, but you should use typed views and TextboxFor when possible.

What is HTML EditorFor in MVC?

ASP.NET MVC includes the method that generates HTML input elements based on the datatype. The Html. Editor() or Html. EditorFor() extension methods generate HTML elements based on the data type of the model object's property.


2 Answers

You cannot have two extension methods with the same name and the same signature at the same time. You could put your extension method into a custom namespace and use this namespace instead of the default one (System.Web.Mvc.Html) in your web.config:

<pages>
    <namespaces>
        <!--<add namespace="System.Web.Mvc.Html"/>-->
        <add namespace="YourCompany.Web.Mvc.Html"/>
    </namespaces>
</pages>

but if you do this you will lose all the other extension methods and you will need to override them in your custom namespace.

like image 137
Darin Dimitrov Avatar answered Sep 28 '22 05:09

Darin Dimitrov


Short answer, no, you can't "replace" an existing extension method.

Longer answer, you might be able to do so by some extremely evil reflection...Although I highly doubt even this would work. Something along these lines:

        // Get the handle for the RuntimeMethodInfo type
        var corlib = Assembly.GetAssembly(typeof (MethodInfo));
        var corlibTypes = corlib.GetModules().SelectMany(mod => mod.FindTypes((a, b) => true, null));
        Type rtmiType = corlibTypes.Where(t => t != null && t.FullName != null && t.FullName.Contains("Reflection.RuntimeMethodInfo")).First();

        // Find the extension method
        var methods = typeof (Html).GetMethods(BindingFlags.Static | BindingFlags.Public);
        foreach (var methodInfo in methods)
        {
            if (methodInfo.Name == "TextBoxFor")
            {
                // Try to monkeypatch this to be private instead of public
                var methodAttributes = rtmiType.GetField("m_methodAttributes", BindingFlags.NonPublic | BindingFlags.Instance);
                if(methodAttributes != null)
                {
                    var attr = methodAttributes.GetValue(methodInfo);
                    attr = ((MethodAttributes)attr) | MethodAttributes.Private;
                    methodAttributes.SetValue(methodInfo, attr);                
                }
            }
        }
like image 32
JerKimball Avatar answered Sep 28 '22 04:09

JerKimball