Wednesday, December 22, 2010

Setting Textbox MaxLength Using StringLengthAttribute

One of the great things about ASP.Net MVC is the ability to do model validation using attributes.

Unfortunately in the case of the maximum length of a textbox, you have to specify a StringLengthAttribute for validation, but also set the MaxLength on the textbox if you want to prevent the user from typing too many characters in the first place.

I think a better idea would be to use the StringLengthAttribute to populate the MaxLength of the textbox automatically.  That way you're only specifying the length once.

In order to achieve this you need to either change the Mvc source code for the TextBoxFor method or create a new method.  I will demonstrate how to create a new method.

Creating the html helper method

You can add more methods to the HtmlHelper class by using an extension method.

public static MvcHtmlString CustomTextBoxFor<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression,
    IDictionary<string, object> htmlAttributes)
{
    if (htmlAttributes == null)
    {
        htmlAttributes = new Dictionary<string, object>();
    }

    ModelMetadata modelMetadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
    IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(modelMetadata, htmlHelper.ViewContext);

    foreach (ModelValidator validator in validators)
    {
        if (validator is StringLengthAttributeAdapter)
        {
            int maxLength = validator.AsDynamic().Attribute.MaximumLength;
            htmlAttributes["MaxLength"] = maxLength;
        }

        return htmlHelper.TextBox(ExpressionHelper.GetExpressionText(expression),
                modelMetadata.Model,
                htmlAttributes);
    }
}
The method works by getting all the validators for the property and checking to see if any of them is of type StringLengthAttributeAdapter.  If one is found, the MaxLength property of the attribute is used to add a MaxLength HtmlAttribute.  Afterwards the framework's TextBox method is used to do all the normal work.

The only strange piece of code is the part that says validator.AsDynamic ()This uses the PrivateReflectionDynamicObjectExtensions from David Ebbo to access the MaxLength property from the attribute, which is for some strange reason a private property.

Having implemented this method, you can simply call Html.CustomTextBoxFor in the same way you would have called Html.TextBoxFor.