MVC DisplayFormatAttribute - DataFormatString for strings - model-view-controller

I have a string stored in my DB that is always 12 characters (digits) long. I want it to be displayed on screen as :
"###/####/####"
I like to use a DisplayFormatAttribute
[DisplayFormat(DataFormatString = "{0:##/####/#####}", ApplyFormatInEditMode = true)]
But the provided DataFormatString does not seems to be working.
EDIT
I tought solving this by creating a customer DisplayFormatAttribute, but this seems not to be that obvious.
Any suggestions ?

I had a quite similar problem, and solved it by using the UIHint attribute in my viewmodel class. In addition I then created a formatter in my EditorTemplates folder. (MVC looks for that folder by default i think).
So what happens is that the rendring engine replace the editor in my view with the formatter.
My example is for bankaccount number with 11 digits, so you have to modify it slightly for your case. The backend db only accept 11 digits with no separators, so therefore i remove these before i save.
In my view
#Html.EditorFor(m => m.BankAccount)
In folder Views/EditorTemplates
#model String
#{
Script.Require("common");
}
#{String temp = String.Empty;}
#if (!String.IsNullOrEmpty(Model))
{
if (Model.Length == 11)
{
temp = String.Format("{0: ####-##-##-###}", Convert.ToInt64(Model)).Replace("-",".");
}
else
{
temp = Model;
}
}
<input type="text" id="BankAccount" name="BankAccount" class="form-control span6" onkeypress="NumberFilter(event, '.');" value="#temp" />
ViewModel
private string _BankAccount;
[UIHint("_BankAccountFormatter")]
public string BankAccount
{
get
{
return _BankAccount;
}
set
{
if (!String.IsNullOrEmpty(value))
{
_BankAccount = value;
_BankAccount = _BankAccount.Trim();
_BankAccount = _BankAccount.Replace(".", "");
}
}
}

Related

Populating Umbraco Contour forms from using cookie data

We're currently using Umbraco version 7.1.4 assembly: 1.0.5261.28127 with Contour version 3.0.26
I'm trying to populate a contour form with information pulled from a database, but dependent on a user cookie (the cookie hold the primary key for the record in the database).
To implement this I'm looking at writing a custom field type (well a bunch of them, one for each data field) which examines the cookie makes the db request and then populates the textbox with the value (users name/address/etc).
I've managed to add custom setting to a control and have it display the value that's populated at design time, but I can't seem to amend that value at run time.
I'm happy to post the code if relevant, but my question is. Am I barking up the wrong tree? is this the best way to handle this or would it even work?
Any pointers would be most welcome
Thanks
EDIT
Thanks Tim,
I've now managed to break it in such a way it's not even rendering the controls (the debug message is saying the SVT value doesn't exist).
This just (or should) just populate the form with the current date/time just to get something working.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Forms.Core;
using System.Web.UI.WebControls;
namespace Custom.FieldType
{
public class CustomTextfield : Umbraco.Forms.Core.FieldType
{
public CustomTextfield()
{
//Provider
this.Id = new Guid("b994bc8b-2c65-461d-bfba-43c4b3bd2915");
this.Name = "Custom Textfield";
this.Description = "Renders a html input fieldKey"; //FieldType
this.Icon = "textfield.png";
this.SVT = DateTime.Now.ToLongTimeString();
}
public System.Web.UI.WebControls.TextBox tb;
public List<Object> _value;
[Umbraco.Forms.Core.Attributes.Setting("SVT", description = "the SVT")]
public string SVT { get; set; }
public override WebControl Editor
{
get
{
tb.TextMode = System.Web.UI.WebControls.TextBoxMode.SingleLine;
tb.CssClass = "text gaudete";
if (_value.Count > 0)
tb.Text = _value[0].ToString();
SVT = DateTime.Now.ToLongTimeString();
tb.Text = tb.Text + SVT;
return tb;
}
set { base.Editor = value; }
}
public override List<Object> Values
{
get
{
if (tb.Text != "")
{
_value.Clear();
_value.Add(tb.Text);
}
return _value;
}
set { _value = value; }
}
public override string RenderPreview()
{
return
"<input type=\"text\" id=\"text-content\" class=\"text\" maxlength=\"500\" value=\"" + this.SVT + "\" />";
}
public override string RenderPreviewWithPrevalues(List<object> prevalues)
{
return RenderPreview();
}
public override bool SupportsRegex
{
get { return true; }
}
}
}
And the view is
#model Umbraco.Forms.Mvc.Models.FieldViewModel
#{
var widthSetting = Model.AdditionalSettings.FirstOrDefault(s => s.Key.Equals("Width"));
string width = (widthSetting == null) ? null : widthSetting.Value;
var textSetting = Model.AdditionalSettings.FirstOrDefault(s => s.Key.Equals("SVT"));
string widthTXT = (textSetting == null) ? null : textSetting.Value;
}
<input type="text" name="#Model.Name" id="#Model.Id" class="text" maxlength="500"
value="#{if(!string.IsNullOrEmpty(widthTXT)){<text>#(SVT)</text>}}"
#{if(Model.Mandatory || Model.Validate){<text>data-val="true"</text>}}
#{if (Model.Mandatory) {<text> data-val-required="#Model.RequiredErrorMessage"</text>}}
#{if (Model.Validate) {<text> data-val-regex="#Model.InvalidErrorMessage" data-regex="#Model.Regex"</text>}}
/>
The code is mostly cobbled together from online tutorials which is why the naming is abysmal but if I can get something to populate the text box on the clients side then I can start the process of refactoring (well scrapping this demo version and writing a real version)
Thanks.
EDIT2
I was able to fix the error stopping the view loading thanks to the pointer from Tim, the new view looks as follows
#model Umbraco.Forms.Mvc.Models.FieldViewModel
#{
var textSetting = Model.AdditionalSettings.FirstOrDefault(s => s.Key.Equals("SVT"));
string widthTXT = (textSetting == null) ? null : textSetting.Value;
}
<input type="text" name="#Model.Name" id="#Model.Id" class="text" maxlength="500"
value="#{if(!string.IsNullOrEmpty(widthTXT)){<text>#(widthTXT)</text>}else{<text>Unknown</text>}}"
#{if(Model.Mandatory || Model.Validate){<text>data-val="true"</text>}}
#{if (Model.Mandatory) {<text> data-val-required="#Model.RequiredErrorMessage"</text>}}
#{if (Model.Validate) {<text> data-val-regex="#Model.InvalidErrorMessage" data-regex="#Model.Regex"</text>}}
/>
And just displays "Unknown" in the text box
thanks again.

How can I make ASP.NET MVC 3 render Html.CheckBoxFor and the corresponding hidden element with reversed values?

I'm using ASP.NET MVC3 with Razor.
I have a boolean value in my model which I would like to render as a check box in my view. However, I would like the check box to indicate the reverse of the boolean state. In other words, selecting the check box should set the bound model object to false, not true, when the form is submitted. And vice versa.
I can do this to set the value attribute on the rendered check box input element:
#Html.CheckBoxFor(model => model.MyBoolean, new { value = "false" })
But the hidden input element that is automatically created still has a value of false. Thus they both have a value of false, which means the bound model object is always set to false.
So how can I force the HTML helper to set the hidden element to true and the check box element to false?
I know that (a) I could alter the model and the database, (b) I could alter the values with javascript just prior to submission, and (c) I could swap whatever value is received in the controller after submission. I may do one of these, but I'm not asking for possible solutions; I'm asking whether it is possible to make the HTML helper do as I wish. I have searched extensively and haven't seen this addressed anywhere in official or unofficial sources. It seems like they should have a "swap" option or something.
class ViewModel {
public bool MyBoolean {get;set;}
public bool DisplayValue {
get {
return ! MyBoolean ;
}
set {
MyBoolean = !value;
}
}
}
And bind to the DisplayValue as it's setter updates you MyBoolean property anyway.
EDIT:
After reading your question again:.
You could use HtmlHelper to do that - but instead of using a CheckBox you could use a dropdown. The dropdown will define the "oppisite" values and text.
myModelInstance.PossibleValues = new[] { new SelectListItem { Value = "false", Text = "Not false" }, new SelectListItem { Value = "true", Text = "Not true" } };
Notice how the description is the opposite meaning of what you want the model to be. So for eg. for true you may have text as "Hidden" and false you may have the text as "Visible", true for "Disabled" and false for "Enabled" etc.
Then in your View:
#Html.DropDownList(Model.MyBoolean.ToString(), Model.PossibleValues)
The model will be updated with the correct value without have to do boolean toggles before viewing or updating.
For future readers, in my own opinion, HtmlHelpers are designed to render Html (as the name suggests). My preference for creating different way to render items is to create EditFor and DisplayFor templates. To make sure this is highly reusable, I also create model designed specifically for these templates. With your design, my models and templates might look like:
/Models/Controller/ControllerActionViewModel.cs
public class ControllerActionViewModel
{
public ControllerActionViewModel()
{
this.CheckboxBoolTemplate = new CheckboxBoolTemplate(false, true);
}
[Display(Name = "My Boolean")]
public SelectBoolTemplate MyBoolean { get; set; }
}
/TemplateModels/ControllerActionViewModel.cs
public sealed class SelectBoolTemplate
{
private bool _valuesSwapped = false;
private bool? _value;
private bool _defaultValue = false;
public SelectBoolTemplate()
{
}
public SelectBoolTemplate(bool valuesSwapped)
{
this._valuesSwapped = valuesSwapped)
}
public SelectBoolTemplate(bool defaultValue, bool valuesSwapped)
{
this._defaultValue = defaultValue;
this._valuesSwapped = valuesSwapped)
}
public bool Value
{
get
{
if (this._value.HasValue)
{
return this._value.Value
}
return this._defaultValue;
}
set
{
this._value = value;
}
}
}
/Views/Shared/EditorTemplates/SelectBoolTemplate.cshtml
#model SelectBoolTemplate
#{
string propertyName = ViewContext.ViewData.ModelMetadata.PropertyName;
string fullPropertyName = ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix;
string labelText = ViewContext.ViewData.ModelMetadata.DisplayName
?? ViewContext.ViewData.ModelMetadata.PropertyName;
}
#Html.LabelForModel()
#Html.Checkbox(fullPropertyName, Model.Value)
I know this is too late for the original query but for anyone reading in future there's an alternative way to handle checkboxes with reversed false/true for checked/unchecked which requires no changes to the model - create a checkbox for false and a hidden field for true
<input id="#Html.IdFor(model => model.BoolProperty)" name="#Html.NameFor(model => model.BoolProperty)" type="checkbox" value="false" #(Model.BoolProperty? "" : "checked=\"checked\"" ) />
<input name="#Html.NameFor(model => model.BoolProperty)" type="hidden" value="true" />

How can I utilise my custom DisplayTemplate with non-text fields so that it doesn't override existing values?

I've been following this guide on creating custom display attributes (specifically extra html attributes) to apply to the properties in my ViewModel. I have overridden both String and Boolean in the EditorTemplates folder. The editor template checks to see if a value has been set/the display attribute has been used - and adds the additional html attributes.
I'm getting stuck on the Boolean override when performing an edit action though. Regardless of whether or not I apply the attribute to a string, the ViewModel always maps with the correct existing data. This isn't true with any other form input type, due to the way the templates have been written by changing the type attribute inside a TextBoxFor.
I've been writing this primarily because I have been digging into knockout, and wanted an easy way to apply the data-bind attribute to strongly-typed views - if there's a better way please let me know!
Attribute Code:
public class Knockout : Attribute
{
public string DataBind { get; set; }
public string InputType { get; set; }
/*
Example:
Knockout("checked: showUploader", "checkbox")
Adds the HTML attributes data-bind="checked: showUploader" type="checkbox"
*/
public Knockout(string dataBind, string inputType)
{
this.DataBind = dataBind;
this.InputType = inputType;
}
public Dictionary<string, object> OptionalAttributes()
{
var options = new Dictionary<string, object>();
if(!string.IsNullOrWhiteSpace(DataBind))
{
options.Add("data-bind", DataBind);
}
if (!string.IsNullOrWhiteSpace(InputType))
{
options.Add("type", InputType);
}
return options;
}
}
Template Code
#using CumbriaMD.Infrastructure.ViewModels.DisplayAttributes
#{
var key = "Knockout";
}
#if (ViewData.ModelMetadata.AdditionalValues.ContainsKey(key))
{
var knockout = ViewData.ModelMetadata.AdditionalValues[key] as Knockout;
#Html.TextBoxFor(model => model, knockout.OptionalAttributes())
}
else
{
/*
When the attribute is not present, the default action is the following - which seems to
be overriding the data mapped from the database:
*/
#Html.TextBoxFor(model => model, new { type="checkbox" })
}
Found the answer nested in this beauty of a question!
My working template for boolean values now looks like:
#using CumbriaMD.Infrastructure.ViewModels.DisplayAttributes
#{
var key = "Knockout";
bool? value = null;
if(ViewData.Model != null)
{
value = Convert.ToBoolean(ViewData.Model, System.Globalization.CultureInfo.InvariantCulture);
}
}
#if (ViewData.ModelMetadata.AdditionalValues.ContainsKey(key))
{
var knockout = ViewData.ModelMetadata.AdditionalValues[key] as Knockout;
#Html.CheckBox("", value ?? false, knockout.OptionalAttributes())
}
else
{
#Html.CheckBox("", value ?? false, new { #class = "check-box" })
}

EditorFor on nullable DateTime - "Nullable object must have a value."

I am attempting to display a form that allows a user to input a new assignment for a person. I'm using a DateTime.cshtml EditorTemplate to handle DateTime values for the assignment. The non-nullable DateTime works fine. The nullable DateTime causes an "InvalidOperationException: Nullable object must have a value."
I have a simple viewmodel that looks like this:
AssignmentViewModel.cs:
public Person Person { get; set; }
public Assignment NewAssignment { get; set; }
Assignment.cs contains:
public DateTime AssignmentStartDate { get; set; }
public DateTime? AssignmentEndDate { get; set; }
My AssignmentController Create() method looks like:
public ViewResult Create(int personId)
{
Person person = personRepository.GetPersonById(personId);
var newAssignment = new AssignmentViewModel { Person = person, NewAssignment = new Assignment() };
return View(newAssignment);
}
My Create.cshtml view looks like this:
#model AssignmentViewModel
#using (Html.BeginForm("Create", "Assignment"))
{
#Html.Hidden("NewAssignment.PersonId", Model.Person.PersonId)
#Html.LabelFor(x => x.NewAssignment.AssignmentStartDate):
#Html.EditorFor(x => x.NewAssignment.AssignmentStartDate.Date, new { cssClass = "datePicker" })
<br />
#Html.LabelFor(x => x.NewAssignment.AssignmentEndDate):
#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.Value.Date, new { cssClass = "datePicker" })
<br />
<input type="submit" value="Send />
}
My DateTime.cshtml EditorTemplate looks like:
#model DateTime?
#{
String modelValue = "";
if (Model.HasValue)
{
if (Model.Value != DateTime.MinValue)
{
modelValue = Model.Value.ToShortDateString();
}
}
}
#Html.TextBox("", modelValue, new { #class = "datePicker" })
When I attempt to load the Create view, I get the exception mentioned above on the line "#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.Value)".
You may be wondering why I'm passing in AssignmentEndDate.Value.Date instead of just passing in AssignmentEndDate; the reason is because I'm trying to get to the point where I'm splitting DateTime into Date and a TimeOfDay field and recombine them with a DateTimeModelBinder. I am using a similar technique to the one shown here and here.
I -can- bypass the error, by changing my controller Create() method to instantiate the ViewModel with AssignmentEndDate set to DateTime.MinValue, but this seems completely wrong for a nullable DateTime:
var newAssignment = new AssignmentViewModel
{
Person = person,
NewAssignment = new Assignment { AssignmentEndDate = DateTime.MinValue }
};
Something strange happens after I "bypass" the error by supplying a value for the nullable DateTime; the un-required nullable DateTime property (AssignmentEndDate.Date) fails client side validation. Trying to submit the form highlights the field in red.
How can I handle this correctly?
The problem is that you're trying to retrieve the AssignmentEndDate.Value.Date, but AssignmentEndDate is null, which results in this error.
Since your editor template accepts a DateTime?, you should just pass along the AssignmentEndDate. In other words, remove the .Value.Date from the view:
#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, new { cssClass = "datePicker" })
Since your editor template is using ToShortDateString(), there's no need to "truncate" the time from the date at all.
Update
Regarding your desire to have separate "Date" and "Time" editors:
You can do this 2 ways.
1 - Your current DateTime? editor renders a field for the Model.Value.Date, so you could simply extend this to also render a field for the Model.Value.TimeOfDay. Example:
#{
DateTime? modelDate = (Model == null) ? (DateTime?)null : Model.Value.Date;
TimeSpan? modelTime = (Model == null) ? (TimeSpan?)null : Model.Value.TimeOfDay;
}
#Html.TextBox(..., modelDate, new{#class="datePicker"})
#Html.TextBox(..., modelTime, new{#class="timePicker"})
2 - You could split the above functionality into 2 separate editors, "DateOnly" and "TimeOnly". Then, update your view to call both editors:
#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, "DateOnly")
#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, "TimeOnly")
The choice is up to you, and whether you want to keep the Date and Time parts separate or together, but this is how I'd go about solving this problem.
create a DateTime.cshtml in your Shared/DisplayTemplate folder
#model Nullable<DateTime>
#(Model != null ? string.Format(ViewData.ModelMetadata.DisplayFormatString ?? "{0:d}", Model) : string.Empty)
this supports metadata from datannotations to be used if found.
UPDATE: GetValueOrDefault treats it as a DateTime and therefore the required Field validators are getting attached, because the original expression is for a datetime not a nullable datetime.
Therefore the solution below doesn't work.
Like the asker, I also used the DateTimeModelBinder from here:
Here's the Link
This is how I solved a similar situation:
#Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.GetValueOrDefault().Date)
And this is what my DateTime EditorTemplate looks like:
#model DateTime
#Html.TextBox("", Model != default(DateTime) ? Model.ToShortDateString() : String.Empty, new { #class = "datepicker", #maxlength = "10" })

How to read property data annotation value in .NET MVC

I Just starting out w/ ASP.NET MVC 3 and I am trying to render out the following HTML for the string properties on a ViewModel on the create/edit view.
<input id="PatientID" name="PatientID" placeholder="Patient ID" type="text" value="" maxlength="30" />
Each value ties back to the property on the ViewModel, id & name are the property name, placeholder is the Display attribute, value is the value of the property, and maxlength is the StringLength attribute.
Instead of typing out the above HTML w/ the correct values for each of my string properties I thought I would try to create an EditorTemplate by the name of SingleLineTextBox and use UIHint on my string properties or pass the name of the view when I call EditFor. So far so good, except I can't figure out how to get the maxlength value off the StringLength attribute.
Here is the code I have so far:
<input id="#ViewData.ModelMetadata.PropertyName" name="#ViewData.ModelMetadata.PropertyName" placeholder="#ViewData.ModelMetadata.DisplayName" type="text" value="#ViewData.Model" maxlength="??" />
As you can see, not sure how to set maxlength value. Anyone know how?
Also, am I going about this the best way? As I said before I could just write out the plain HTML myself for each property on the page. I've looked at using TextBoxFor it wasn't setting the maxlength and was adding a bunch of validation markup to the HTML output because of the StringLength attribute which I do not want. Another option I saw was extensions/helpers off the HTML class.
A full code sample for tvanfosson's answer:
Model:
public class Product
{
public int Id { get; set; }
[MaxLength(200)]
public string Name { get; set; }
EditorTemplates\String.cshtml
#model System.String
#{
var metadata = ViewData.ModelMetadata;
var prop = metadata.ContainerType.GetProperty(metadata.PropertyName);
var attrs = prop.GetCustomAttributes(false);
var maxLength = attrs.OfType<System.ComponentModel.DataAnnotations.MaxLengthAttribute>().FirstOrDefault();
}
<input id=#Html.IdForModel()#(metadata.IsRequired ? " required" : "")#(maxLength == null ? "" : " maxlength=" + maxLength.Length) />
HTML output:
<input id=Name maxlength=200 />
Ugly but it works. Now let's abstract it and clean it up a bit. Helper class:
public static class EditorTemplateHelper
{
public static PropertyInfo GetPropertyInfo(ViewDataDictionary viewData)
{
var metadata = viewData.ModelMetadata;
var prop = metadata.ContainerType.GetProperty(metadata.PropertyName);
return prop;
}
public static object[] GetAttributes(ViewDataDictionary viewData)
{
var prop = GetPropertyInfo(viewData);
var attrs = prop.GetCustomAttributes(false);
return attrs;
}
public static string GenerateAttributeHtml(ViewDataDictionary viewData, IEnumerable<Delegate> attributeTemplates)
{
var attributeMap = attributeTemplates.ToDictionary(t => t.Method.GetParameters()[0].ParameterType, t => t);
var attrs = GetAttributes(viewData);
var htmlAttrs = attrs.Where(a => attributeMap.ContainsKey(a.GetType()))
.Select(a => attributeMap[a.GetType()].DynamicInvoke(a));
string s = String.Join(" ", htmlAttrs);
return s;
}
}
Editor Template:
#model System.String
#using System.ComponentModel.DataAnnotations;
#using Brass9.Web.Mvc.EditorTemplateHelpers;
#{
var metadata = ViewData.ModelMetadata;
var attrs = EditorTemplateHelper.GenerateAttributes(ViewData, new Delegate[] {
new Func<StringLengthAttribute, string>(len => "maxlength=" + len.MaximumLength),
new Func<MaxLengthAttribute, string>(max => "maxlength=" + max.Length)
});
if (metadata.IsRequired)
{
attrs.Add("required");
}
string attrsHtml = String.Join(" ", attrs);
}
<input type=text id=#Html.IdForModel() #attrsHtml />
So you pass in an array of Delegates, and for each entry use a Func<AttributeTypeGoesHere, string>, and then return whatever HTML string you wanted for each attribute.
This actually decouples well - you can map only the attributes you care about, you can map different sets for different parts of the same HTML, and the final usage (like #attrsHtml) doesn't harm readability of the template.
Instead of the StringLength attribute (because it's a validator attribute not a metadata provider) you can use the AdditionalMetadata attribute. Sample usage:
public class ViewModel
{
[AdditionalMetadata("maxLength", 30)]
public string Property { get; set; }
}
Basically it puts the value 30 under the key maxLength in the ViewData.ModelMetadata.AdditionalValues dictionary. So you can use it your EditorTemplate:
<input maxlength="#ViewData.ModelMetadata.AdditionalValues["maxLength"]" id="#ViewData.ModelMetadata.PropertyName" name="#ViewData.ModelMetadata.PropertyName" placeholder="#ViewData.ModelMetadata.DisplayName" type="text" value="#ViewData.Model" />
To do this you'll need to create your own HtmlHelper extension and use reflection to get at the attributes on the model property. Look at the source code at http://codeplex.com/aspnet for the existing ...For() HtmlHelper extensions. You'll need to get the PropertyInfo object for the model property using the expression that is passed in as the argument. They have several helper classes that should serve as templates for this. Once you have that, use the GetCustomAttributes method on the PropertyInfo to find the StringLength attribute and extract it's value. Since you'll be using a TagBuilder to create the input, add the length as an attribute via the TagBuilder.
...
var attribute = propInfo.GetCustomAttributes(typeof(StringLengthAttribute),false)
.OfType<StringLengthAttribute>()
.FirstOrDefault();
var length = attribute != null ? attribute.MaximumLength : 20; //provide a default
builder.Attributes.Add("maxlength",length);
...
return new MvcHtmlString( builder.ToString( TagRenderMode.SelfClosing ) );
}
See my comment on why I think this is a bad idea.
An much simpler solution is to implement a custom DataAnnotationsModelMetadataProvider like this:
internal class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
ModelMetadata modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
var maxLengthAttribute = attributes.OfType<MaxLengthAttribute>().SingleOrDefault();
if (maxLengthAttribute != null)
{
modelMetadata.AdditionalValues.Add("maxLength", maxLengthAttribute.Length);
}
return modelMetadata;
}
}
In the template you can simply use:
object maxLength;
ViewData.ModelMetadata.AdditionalValues.TryGetValue("maxLength", out maxLength);
You can get the StringLength Validator from within an Editor Template, here are some examples:
https://jefferytay.wordpress.com/2011/12/20/asp-net-mvc-string-editor-template-which-handles-the-stringlength-attribute/
What I used and tested, as a result from the article above can be seen in my answer below (tested with MVC 5, EF 6) :
ASP.NET MVC 3 - Data Annoation and Max Length/Size for Textbox Rendering
Without being specific, I've personally had some mixed results with attempts to implement some other approaches, and I don't find either claimed method particular long; however, I did think some of the other approached looked a little "prettier".
#using System.ComponentModel.DataAnnotations
#model string
#{
var htmlAttributes = ViewData["htmlAttributes"] ?? new { #class = "checkbox-inline" };
var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
if (!attributes.ContainsKey("maxlength"))
{
var metadata = ViewData.ModelMetadata;
var prop = metadata.ContainerType.GetProperty(metadata.PropertyName);
var attrs = prop.GetCustomAttributes(false);
var maxLength = attrs.OfType<MaxLengthAttribute>().FirstOrDefault();
if (maxLength != null)
{
attributes.Add("maxlength", maxLength.Length.ToString());
}
else
{
var stringLength = attrs.OfType<StringLengthAttribute>().FirstOrDefault();
if (stringLength != null)
{
attributes.Add("maxlength", stringLength.MaximumLength.ToString());
}
}
}
}
#Html.TextBoxFor(m => m, attributes)

Resources