maxlength attribute of a text box from the DataAnnotations StringLength in Asp.Net MVC

前端 未结 7 663
攒了一身酷
攒了一身酷 2020-11-30 18:33

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 objec

相关标签:
7条回答
  • 2020-11-30 19:03

    While I'm personally loving jrummel's jquery fix, here's another approach to keeping a single-source-of-truth up in your model...

    Not pretty, but.. has worked o.k. for me...

    Instead of using property decorations, I just define some well-named public constants up in my model library/dll, and then reference them in my view via the HtmlAttributes, e.g.

    Public Class MyModel
    
        Public Const MAX_ZIPCODE_LENGTH As Integer = 5
    
        Public Property Address1 As String
    
        Public Property Address2 As String
    
        <MaxLength(MAX_ZIPCODE_LENGTH)>
        Public Property ZipCode As String
    
        Public Property FavoriteColor As System.Drawing.Color
    
    End Class
    

    Then, in the razor view file, in the EditorFor... use an HtmlAttirubte object in the overload, supply the desired max-length property and referenece the constant.. you'll have to supply the constant via a fully qualied namespace path... MyCompany.MyModel.MAX_ZIPCODE_LENGTH.. as it won't be hanging right off the model, but, it works.

    0 讨论(0)
  • 2020-11-30 19:04

    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" })%>
    
    0 讨论(0)
  • 2020-11-30 19:07

    Here are some static methods you can use to get the StringLength, or any other attribute.

    using System;
    using System.Linq;
    using System.Reflection;
    using System.ComponentModel.DataAnnotations;
    using System.Linq.Expressions;
    
    public static class AttributeHelpers {
    
    public static Int32 GetStringLength<T>(Expression<Func<T,string>> propertyExpression) {
        return GetPropertyAttributeValue<T,string,StringLengthAttribute,Int32>(propertyExpression,attr => attr.Length);
    }
    
    //Optional Extension method
    public static Int32 GetStringLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) {
        return GetStringLength<T>(propertyExpression);
    }
    
    
    //Required generic method to get any property attribute from any class
    public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T,TOut>> propertyExpression,Func<TAttribute,TValue> valueSelector) where TAttribute : Attribute {
        var expression = (MemberExpression)propertyExpression.Body;
        var propertyInfo = (PropertyInfo)expression.Member;
        var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute),true).FirstOrDefault() as TAttribute;
    
        if (attr==null) {
            throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name);
        }
    
        return valueSelector(attr);
    }
    
    }
    

    Using the static method...

    var length = AttributeHelpers.GetStringLength<User>(x => x.Address1);
    

    Or using the optional extension method on an instance...

    var player = new User();
    var length = player.GetStringLength(x => x.Address1);
    

    Or using the full static method for any other attribute...

    var length = AttributeHelpers.GetPropertyAttributeValue<User,string,StringLengthAttribute,Int32>(prop => prop.Address1,attr => attr.MaximumLength);
    

    Inspired by the answer here... https://stackoverflow.com/a/32501356/324479

    0 讨论(0)
  • 2020-11-30 19:08

    If you're using unobtrusive validation, you can handle this client side as well:

    $(document).ready(function ()
    {
        $("input[data-val-length-max]").each(function ()
        {
            var $this = $(this);
            var data = $this.data();
            $this.attr("maxlength", data.valLengthMax);
        });
    });
    
    0 讨论(0)
  • 2020-11-30 19:08

    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
            var 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...

    Edit. The Step 3 can start to get ugly if you want to add more stuff to the textbox. If this is your case you can do the following:

    <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
    <%
        IDictionary<string, object> Attributes = new Dictionary<string, object>();
        if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("MaxLength")) {
            Attributes.Add("MaxLength", (int)ViewData.ModelMetadata.AdditionalValues["MaxLength"]);
        }
        if (ViewData.ContainsKey("style")) {
            Attributes.Add("style", (string)ViewData["style"]);
        }
        if (ViewData.ContainsKey("title")) {
            Attributes.Add("title", (string)ViewData["title"]);
        }
    %>
    <%: Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, Attributes)%>
    
    0 讨论(0)
  • 2020-11-30 19:12

    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; }
    
        /* ... */
      }
    }
    
    0 讨论(0)
提交回复
热议问题