I'd like the attribute that I set in the ViewModel to control this if
possible.
ASP.NET MVC provides an extensible system for doing exactly this. Here is what you need to do:
- Implement a custom
ModelMetadataProvider.
- Look for the
StringLengthAttribute or the MaxLengthAttribute, extract the information and add it to the ModelMetadata.
- Provide a custom Editor template that makes use of the information.
Step 1: Implement a custom ModelMetadataProvider.
Create a class that derives from ModelMetadataProvider. Typically you would derive from the DataAnnotationsModelMetadataProvider as this provides some default functionality which means you only have to override a single method called CreateMetadata.
Step 2: Extract the information:
To get the information, you need to look for the attribute, extract the maximum length information and add it to the AdditionalValues dictionary of the ModelMetadata. The implementation would look something like this (this is the entire implementation):
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
// Call the base class implementation to create the default metadata...
var metadata = base.CreateMetadata(
attributes,
containerType,
modelAccessor,
modelType,
propertyName);
// Extract the stringLengthAttribute (you can do the same for the
// MaxLengthAttribute if you want).
var attr = attributes
.OfType<StringLengthAttribute>()
.First();
// This could be getting called on a property that doesn't have the
// attribute so make sure you check for null!
if (attr != null)
{
metadata.AdditionalValues["maxLength"] = attr.MaximumLength;
}
return metadata;
}
}
In order for ASP.NET MVC to use this you need to register it in the Application_Start method in Global.asax.
ModelMetadataProviders.Current = new CustomModelMetadataProvider();
Step 3: Create a custom editor template.
You now need to create a view that uses the information. Create a new view called String in the Views\Shared\ folder.
String.cshtml
@{
object maxLength;
if (!ViewData.ModelMetadata.AdditionalValues
.TryGetValue("maxLength", out maxLength))
{
maxLength = 0;
}
var attributes = new RouteValueDictionary
{
{"class", "text-box single-line"},
{ "maxlength", (int)maxLength },
};
}
@Html.TextBox("", ViewContext.ViewData.TemplateInfo.FormattedModelValue, attributes)
When you run your application you will get the following HTML output by calling @Html.EditorFor.
<input class="text-box single-line" id="Extension" maxlength="6" name="Extension" type="text" value="" />
If you want to know more about the model metadata provider system, Brad Wilson has a series of blog posts that detail how it works (these were written prior to the Razor view engine so some of the view Syntax is a bit funky but otherwise the information is sound).