【问题标题】:maxlength attribute of a text box from the DataAnnotations StringLength in Asp.Net MVCAsp.Net MVC 中 DataAnnotations StringLength 中文本框的 maxlength 属性
【发布时间】:2011-01-24 02:07:49
【问题描述】:

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

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

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

下面的代码sn-ps:

来自模型:

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

有什么办法吗?

【问题讨论】:

  • 不好意思,不知道Data Annonations有什么用?我的意思是,如果长度标准发生变化怎么办?这不能根据一些元数据动态驱动(在运行时)吗?

标签: asp.net-mvc validation


【解决方案1】:

我不知道有任何方法可以在不诉诸反思的情况下实现这一目标。你可以写一个辅助方法:

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”不包含“TextBoxFor”的定义,并且没有扩展方法“TextBoxFor”接受“System”类型的第一个参数.Web.Mvc.HtmlHelper' 可以在这一行找到(您是否缺少 using 指令或程序集引用?):return htmlHelper.TextBoxFor(expression, attributes);
  • 是的,我已经知道了 :) 我还有另一个问题,我的数据注释是在 MetaData 类而不是模型本身上定义的。反射没有把它们捡起来!
  • 感谢您的回答。我遇到了包含 _ 的属性的问题,它们不会自动转换为 -。您知道与手动替换 _ 并填写新的 RouteValueDictionary 不同的方法吗?。
  • 此答案不适用于模型是属性的情况。例如,字符串的编辑器模板。戴夫克莱默的回答如下处理所有情况。
【解决方案2】:

如果您希望它与元数据类一起使用,您需要使用以下代码。我知道它不漂亮,但它完成了工作,并防止你不得不在实体类和视图中编写你的 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】:

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 虽然你的方法会给我验证 - 我真的在输入 maxlength 属性之后因为它会阻止用户在浏览器中输入更多字符并且无论javascript是否正在运行都可以工作在浏览器中。
      • 这正是它的作用。它使用数据最大长度验证属性来设置输入 maxlenth 属性。
      • 我对第一个反射答案感到非常兴奋,但这看起来无需任何复杂的服务器代码即可实现相同的结果。好工作。你应该得到更多的选票。
      • +1 Ajaxed 表单的好主意。我同意 Brian White 的观点,即这个答案值得更多投票。
      • 我错过了关于 OP 需要在没有 javascript 的情况下进行验证的部分。但我很高兴这有助于其他人寻找 javascript 解决方案。
      【解决方案5】:

      虽然我个人喜欢 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
      

      然后,在 razor 视图文件中,在 EditorFor... 在重载中使用 HtmlAttirubte 对象,提供所需的最大长度属性并引用常量.. 您必须通过完全合格的方法提供常量命名空间路径... MyCompany.MyModel.MAX_ZIPCODE_LENGTH.. 因为它不会直接挂在模型上,但是它可以工作。

      【讨论】:

        【解决方案6】:

        我发现 Darin 基于反射的方法特别有用。我发现使用元数据 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);
        }
        

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 2011-08-08
            • 2012-07-09
            • 2013-01-22
            • 2021-05-29
            • 1970-01-01
            • 1970-01-01
            • 2012-06-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多