views:

1063

answers:

3

I am working on an MVC2 application and want to set the maxlength attributes of the text inputs.

I have already defined the stringlength attribute on the Model object using data annotations and it is validating the length of entered strings correctly.

I do not want to repeat the same setting in my views by setting the max length attribute manually when the model already has the information. Is there any way to do this?

Code snippets below:

From the Model:

[Required, StringLength(50)]
public string Address1 { get; set; }

From the View:

<%= Html.LabelFor(model => model.Address1) %>
<%= Html.TextBoxFor(model => model.Address1, new { @class = "text long" })%>
<%= Html.ValidationMessageFor(model => model.Address1) %>

What I want to avoid doing is:

<%= Html.TextBoxFor(model => model.Address1, new { @class = "text long", maxlength="50" })%>

Is there any way to do this?

+4  A: 

I am not aware of any way to achieve this without resorting to reflection. You could write a helper method:

public static MvcHtmlString CustomTextBoxFor<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expression, 
    object htmlAttributes
)
{
    var member = expression.Body as MemberExpression;
    var stringLength = member.Member
        .GetCustomAttributes(typeof(StringLengthAttribute), false)
        .FirstOrDefault() as StringLengthAttribute;

    var attributes = (IDictionary<string, object>)new RouteValueDictionary(htmlAttributes);
    if (stringLength != null)
    {
        attributes.Add("maxlength", stringLength.MaximumLength);
    }
    return htmlHelper.TextBoxFor(expression, attributes);
}

which you could use like this:

<%= Html.CustomTextBoxFor(model => model.Address1, new { @class = "text long" })%>
Darin Dimitrov
I'm gettingError 1 'System.Web.Mvc.HtmlHelper<TModel>' does not contain a definition for 'TextBoxFor' and no extension method 'TextBoxFor' accepting a first argument of type 'System.Web.Mvc.HtmlHelper<TModel>' could be found (are you missing a using directive or an assembly reference?)at this line:return htmlHelper.TextBoxFor<TModel>(expression, attributes);
sabbour
`using System.Web.Mvc.Html`?
Darin Dimitrov
Yeah I figured that out already :)I have another problem though, my data annotations are defined on MetaData classes rather than the Model itself.The reflection is not picking them up!
sabbour
+1  A: 

If you want this to work with a metadata class you need to use the following code. I know its not pretty but it gets the job done and prevents you from having to write your maxlength properties in both the Entity class and the View:

public static MvcHtmlString TextBoxFor2<TModel, TProperty>
(
  this HtmlHelper<TModel> htmlHelper,
  Expression<Func<TModel, TProperty>> expression,
  object htmlAttributes = null
)
{
  var member = expression.Body as MemberExpression;

  MetadataTypeAttribute metadataTypeAttr = member.Member.ReflectedType
    .GetCustomAttributes(typeof(MetadataTypeAttribute), false)
    .FirstOrDefault() as MetadataTypeAttribute;

  IDictionary<string, object> htmlAttr = null;

  if(metadataTypeAttr != null)
  {
    var stringLength = metadataTypeAttr.MetadataClassType
      .GetProperty(member.Member.Name)
      .GetCustomAttributes(typeof(StringLengthAttribute), false)
      .FirstOrDefault() as StringLengthAttribute;

    if (stringLength != null)
    {
      htmlAttr = new RouteValueDictionary(htmlAttributes);
      htmlAttr.Add("maxlength", stringLength.MaximumLength);
    }                                    
  }

  return htmlHelper.TextBoxFor(expression, htmlAttr);
}

Example class:

[MetadataType(typeof(Person.Metadata))]
public partial class Person
{
  public sealed class Metadata
  {

    [DisplayName("First Name")]
    [StringLength(30, ErrorMessage = "Field [First Name] cannot exceed 30 characters")]
    [Required(ErrorMessage = "Field [First Name] is required")]
    public object FirstName { get; set; }

    /* ... */
  }
}
dcompiled
A: 

//I use the CustomModelMetaDataProvider to achieve this

//STEP 1. Add New CustomModelMetadataProvider class public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider {

    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
                                                    Type containerType,
                                                    Func<object> modelAccessor,
                                                    Type modelType,
                                                    string propertyName)
    {
        ModelMetadata metadata = base.CreateMetadata(attributes,
                                                     containerType,
                                                     modelAccessor,
                                                     modelType,
                                                     propertyName);



   //Add MaximumLength to metadata.AdditionalValues collection
        StringLengthAttribute stringLengthAttribute = attributes.OfType<StringLengthAttribute>().FirstOrDefault();
        if (stringLengthAttribute != null)
            metadata.AdditionalValues.Add("MaxLength", stringLengthAttribute.MaximumLength);



        return metadata;
    }
}

}

//STEP 2. In Global.asax Register the CustomModelMetadataProvider

protected void Application_Start() { AreaRegistration.RegisterAllAreas();

        RegisterRoutes(RouteTable.Routes);

        ModelMetadataProviders.Current = new CustomModelMetadataProvider();
    }

//STEP 3. In Views/Shared/EditorTemplates Add a partial view called String.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%if (!ViewData.ModelMetadata.AdditionalValues.ContainsKey("MaxLength")) {%> <%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line" }) %> <%} else { int maxLength = (int)ViewData.ModelMetadata.AdditionalValues["MaxLength"];%> <%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line", MaxLength = maxLength })%> <% } %>

Done...

Randhir