ASP.NET MVC: Create Custom Attributes for MetaData

Posted by: Malcolm Sheridan , on 4/14/2011, in Category ASP.NET MVC
Views: 80412
Abstract: The following article shows you how to create a custom attribute to output additional HTML when using a metadata class in MVC.

One of the strong features to come in MVC 2 was the ability to create metadata classes that allowed you to specify the metadata that was linked to the model. You could decorate properties with the StringLength, Range and RegularExpression attribute. Sometimes you want to add HTML attributes to your elements, say if you want to set the tab index for an input control. You could do this, but then you’re adding extra mark-up that could be placed elsewhere:

html-textboxfor

Wouldn’t it be nicer to omit the anonymous object? I think so and this is one way to do it.

The crux of the code lies with a class that derives from DataAnnotationsModelMetadataProvider. This class implements the default model metadata provider. All you need to do is override CreateMetadata and find the AdditionalValues collection. This is where you’ll insert your own custom object. Here’s the code below:

 

mvc-createmetadata

Calling the base class you can find the metadata for the property. One of the properties is AdditonalValues. This value gets a dictionary that contains additional metadata about the model. Basically what’s happening in the code above is when the view is rendered, the framework will look through all the attributes associated to the property. If it finds an attribute called AdditionalHtml (which we haven’t created yet), it will add it the properties AdditionalValues property. Make sense? Let’s add the AdditionalHtml class now:

mvc-additionalhtml

The class is intended to be used as an attribute, hence why it’s deriving from Attribute. There’s one public method called OptionalAttributes, and all that does is add values to a dictionary and returns that. This dictionary will be used as the input for the AdditionalValues in the previous section. The public properties relate to attributes you can use on HTML input’s, such as readonly,  maxlength and css classes.

To use this new attribute, you need to decorate the properties in your metadata class like this:

mvc-metadata-properties

This class is linked to the model, Employee, by using the MetadataType attribute:

mvc-metadata-type

And finally to use this, I’m using a templated helper to output the view. Because the two properties are strings, the templated helper must be called string.cshtml and reside in the EditorTemplates folder

mvc-templatehelper

The final step is to let the framework know it should use this as the default ModelMetadataProvider. This is done through the global.asax file:

mvc-app-start

When the view is rendered, the framework uses the templated helper to display any properties whose data type is string. If it finds one, it grabs the metadata and checks if it’s been decorated with the AdditionalHtml attribute. If it does, it renders that as the additional html attributes. The result is below:

mvc-view-additionalhtml

I like adding additional html attributes this way as it keeps the mark-up nice and clean. It is allot of work to output simple attributes, but isn’t that part of the fun of programming?

The entire source code of this article can be downloaded over here

Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles
Malcolm Sheridan is a Microsoft awarded MVP in ASP.NET, a Telerik Insider and a regular presenter at conferences and user groups throughout Australia and New Zealand. Being an ASP.NET guy, his focus is on web technologies and has been for the past 10 years. He loves working with ASP.NET MVC these days and also loves getting his hands dirty with jQuery and JavaScript. He also writes technical articles on ASP.NET for SitePoint and other various websites. Follow him on twitter @malcolmsheridan


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by Andrew Gunn on Thursday, April 14, 2011 10:25 AM
Great idea. Totally overkill but I like it. I don't like the idea of a direct link between POCOs and HTML attributes. You could define your models as partial classes (in a separate assembly maybe), and then apply the metadata similar to the way done by Scott Gu for validation (http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx).
Comment posted by Malcolm Sheridan on Thursday, April 14, 2011 6:37 PM
@Andrew

Thanks.  I tend to agree that it is overkill.  The one benefit is all of your code is separated from the front end, so your views are kept "clean".  But on the other hand, what are views for if they're not to contain the HTML.  Anyway this article just shows how this is possible.

Thanks for the feedback.
Comment posted by Fred Coder on Friday, April 22, 2011 12:42 AM
Looks like the same over-engineering approach that has cost so much time and money in Java multi-tiered development.  
Comment posted by Skywalker on Tuesday, May 24, 2011 6:05 AM
Even though I wouldn;t encourage a direct link between POCOs and HTML attributes, this post does show how one can create custom attributes, attach them to a POCO and how to use that attribute from the view. And that is EXACTLY what I was looking for! (In y case, I need a HelpTextAttribute which I want to attach to certain properties of my viewmodels and use that inside a generic Object TemplateEditor). So a BIG thank you for this excellent post!
Comment posted by Skywalker on Tuesday, May 24, 2011 6:09 AM
By the way, I just figured that in my case I could simply use the AdditionalMetadataAttribute to decorate my properties which require helptext
Comment posted by Skywalker on Tuesday, May 24, 2011 6:57 AM
Even better: there is an existing DisplayAttribute which takes a Description parameter (among several other userful ones!). See: http://stackoverflow.com/questions/3702793/using-the-description-metadata-attribute-in-asp-net-mvc for more details.
Comment posted by @echarbeneau on Friday, June 10, 2011 8:10 AM
I don't think this is over kill. Consider this; you could use this on your ViewModels, and as long as it's part of convention for that codebase (ie. your not mixing and matching convention all over your view) I wouldn't have a problem using this as is. Besides this specific scenario, I think it's a great example of how custom attributes work, and I'm sure I'll be using it for something.
Comment posted by Guillermo on Monday, February 4, 2013 2:03 PM
I think it's not far more overkill than the way the framework currently does it. It will use it's own ModelMetaDataProvider anyway, even if you don't specify additional attributes. Still, the way the framework does it seems sort of overkill to me.

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel