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


80

我正在MVC2应用程序上工作,并希望设置文本输入的maxlength属性。

我已经使用数据注释在Model对象上定义了stringlength属性,它可以正确验证输入字符串的长度。

我不希望在模型已经具有信息时通过手动设置max length属性来在视图中重复相同的设置。有什么办法吗?

下面的代码段:

从模型:

[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"/>

有什么办法吗?


抱歉,我不知道数据标注有什么用处?我的意思是,如果长度标准发生变化怎么办?不能基于某些元数据动态地(在运行时)驱动它吗?
shahkalpesh 2010年

Answers:


51

我不知道有什么方法可以不依靠反思。您可以编写一个辅助方法:

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" })%>

我收到错误1“ System.Web.Mvc.HtmlHelper <TModel>”,其中不包含“ TextBoxFor”的定义,也没有扩展方法“ TextBoxFor”接受类型为“ System.Web.Mvc.HtmlHelper <TModel”的第一个参数可以在此行找到>'(您是否缺少using指令或程序集引用?):return htmlHelper.TextBoxFor <TModel>(expression,attributes);
2010年

2
是的,我已经弄清楚了:)但是我还有另一个问题,我的数据注释是在MetaData类而不是Model本身上定义的。反射没有把它们捡起来!
2010年

谢谢你的回答。我在包含_的属性时遇到问题,它们不会自动转换为-。除了手动替换_并填充新的RouteValueDictionary之外,您还知道其他方法吗?
胡安

对于以模型为属性的情况,此答案不起作用。例如,字符串的编辑器模板。Dave Clemmer在下面的回答可以解决所有情况。
迈克尔·布兰登·莫里斯

59

如果您使用的是非侵入式验证,则也可以处理此客户端:

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

尽管您的方法可以使我验证-我确实在将maxlength属性放在输入上之后,因为它可以防止用户在浏览器中放入更多字符,并且无论JavaScript是否在浏览器中运行都可以使用。
Pervez Choudhury'4

5
这正是这样做的。它使用数据最大长度验证属性来设置输入maxlenth属性。
jrummell 2012年

5
我对第一个反思性的答案感到非常兴奋,但这看起来无需任何复杂的服务器代码即可达到相同的结果。做得好。您应该获得更多选票。
布莱恩·怀特

1
+1 Ajaxed表单的好主意。我同意布莱恩·怀特(Brian White)的观点,该答案值得更多投票。
Waleed Eissa

1
我错过了有关OP无需Java验证的部分。但我很高兴这可以帮助其他人寻找JavaScript解决方案。
jrummell 2012年

20

我使用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)%>

8

如果希望它与元数据类一起使用,则需要使用以下代码。我知道它不是很漂亮,但是它可以完成工作,并且使您不必在Entity类和View中都编写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; }

    /* ... */
  }
}

3

当我个人喜欢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属性并引用常量。.您必须通过完全限定的名称空间路径提供常量。 .. MyCompany.MyModel.MAX_ZIPCODE_LENGTH ..因为它不会马上挂在模型上,但是它可以工作。


2

我发现达林基于反射的方法特别有用。我发现使用元数据ContainerType作为获取属性信息的基础要可靠一些,因为可以在mvc编辑器/显示模板(TModel最终是诸如的简单类型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);
}

1

这是一些静态方法,可用于获取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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.