Asp.Net MVC中的DataAnnotations StringLength的文本框的maxlength属性

我正在开发一个MVC2应用程序,并希望设置文本input的最大长度属性。

我已经使用数据注释在Model对象上定义了stringlength属性,它正确地validation了inputstring的长度。

我不想在我的视图中重复相同的设置,通过手动设置最大长度属性模型已经有信息。 有没有办法做到这一点?

下面的代码片段:

从型号:

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

从视图:

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

我想避免做的是:

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

我想要得到这个输出:

 <input type="text" name="Address1" maxlength="50" class="text long"/> 

有没有办法做到这一点?

我不知道有什么办法可以做到这一点,而不是去反思。 你可以写一个辅助方法:

 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); } 

你可以这样使用:

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

如果你使用不显眼的validation,你也可以处理这个客户端:

 $(document).ready(function () { $("input[data-val-length-max]").each(function () { var $this = $(this); var data = $this.data(); $this.attr("maxlength", data.valLengthMax); }); }); 

我使用CustomModelMetaDataProvider来实现这一点

第1步。添加新的CustomModelMetadataProvider类

 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; } } 

第2步。在Global.asax中注册CustomModelMetadataProvider

 protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterRoutes(RouteTable.Routes); ModelMetadataProviders.Current = new CustomModelMetadataProvider(); } 

步骤3.在Views / Shared / EditorTemplates中添加一个名为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 })%> <% } %> 

完成…

编辑。 如果你想添加更多的东西到文本框,步骤3可以开始变得丑陋。 如果这是你的情况,你可以做到以下几点:

 <%@ 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)%> 

如果你想要这个元数据类,你需要使用下面的代码。 我知道它不漂亮,但它完成了工作,并防止您必须在实体类和视图中写入maxlength属性:

 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); } 

示例类

 [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; } /* ... */ } } 

虽然我个人非常喜欢jrummel的jquery修复,但这里有另一种方法来保持模型中的单一来源。

不漂亮,但..已经为我工作好了…

我不是使用属性装饰,而是在模型库/ dll中定义一些有名的公共常量,然后通过HtmlAttributes在我的视图中引用它们,例如

 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 

然后,在剃刀视图文件中,在EditorFor …在重载中使用HtmlAttirubte对象,提供所需的max-length属性并参考常量..您必须通过完全限定的名称空间path提供常量。 .. MyCompany.MyModel.MAX_ZIPCODE_LENGTH ..因为它不会挂在模型上,但是,它的工作原理。

我发现Darin基于反思的方法特别有用。 我发现使用元数据ContainerType作为获取属性信息的基础是可靠一些,因为这个方法可以在mvc编辑器/显示模板(其中TModel最终是一个简单的types,比如string )中被调用。

 public static MvcHtmlString CustomTextBoxFor<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes ) { var metadata = ModelMetadata.FromLambdaExpression( expression, new ViewDataDictionary<TModel>( htmlHelper.ViewDataContainer.ViewData ) ); var stringLength = metadata.ContainerType.GetProperty(metadata.PropertyName) .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); } 

这里有一些静态方法可以用来获取StringLength或其他任何属性。

 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); } } 

使用静态方法…

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

或者在实例上使用可选的扩展方法…

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

或者对其他任何属性使用完整的静态方法…

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

由这里的答案启发… https://stackoverflow.com/a/32501356/324479