I’ve answered a couple questions recently about using the DescriptionAttribute from the System.ComponentModel namespace, as well as creating your own Attributes and using them in HtmlHelpers. I’ve answered each one individually to answer the specific questions with very specific answers to each one. But it got me thinking that MVC has a way to override just about everything and doubtless the MetadataProvider was one of them. A quick search of the code available at codeplex lead me to the ModelMetadataProviders.

I’m going to create a new class based on the DataAnnotationsModelMetadataProvider since I use this particular provider a lot.

public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider {

    protected override ModelMetadata CreateMetadata(
                             IEnumerable<Attribute> attributes,
                             Type containerType,
                             Func<object> modelAccessor,
                             Type modelType,
                             string propertyName) {

        var data = base.CreateMetadata(
                             attributes,
                             containerType,
                             modelAccessor,
                             modelType,
                             propertyName);

        return data;
    }

}

Simple and to the point. Now how do we access the System.ComponentModel.DescriptionAttribute. Well, luckily for us, the attributes parameter of CreateMetadata provides us with all the attributes assigned to our particular property we’re attempting to get data for. Since attributes is IEnumerable we can use linq to find a particular attribute.

protected override ModelMetadata CreateMetadata(
                             IEnumerable<Attribute> attributes,
                             Type containerType,
                             Func<object> modelAccessor,
                             Type modelType,
                             string propertyName) {
    var data = base.CreateMetadata(attributes,
                             containerType,
                             modelAccessor,
                             modelType,
                             propertyName);

    var description = attributes
                .SingleOrDefault(a => typeof(DescriptionAttribute) == a.GetType());
    if (description != null) data
                .Description = ((DescriptionAttribute)description).Description;

    return data;
}

This is great. This will take care of old code that relies on the old DescriptionAttribute rather than the new DataAnnotations DisplayAttribute(Description=”"). What if we wanted to add our own custom information to the ModelMetadata? Well the MVC team has graciously provided us with an AdditionalValues property to the ModelMetadata. In this example I’m going to make a tooltip attribute for the title attribute of an html element.

protected override ModelMetadata CreateMetadata(
                             IEnumerable<Attribute> attributes,
                             Type containerType,
                             Func<object> modelAccessor,
                             Type modelType,
                             string propertyName) {
    var data = base.CreateMetadata(attributes,
                              containerType,
                             modelAccessor,
                             modelType,
                             propertyName);

    var tooltip = attributes
                .SingleOrDefault(a => typeof(TooltipAttribute) == a.GetType());
    if (tooltip != null) data.AdditionalValues
                .Add("Tooltip", ((TooltipAttribute)tooltip).Tooltip);

    return data;
}

Same as before but instead of using an existing property of the ModelMetadata we’re adding a key/value pair to the AdditionalValues property.

Using our new-found powers for awesome

So how do we use this new found ability? First we’re going to set the current provider to our new provider by adding this to our global.asax Application_Start() method.

ModelMetadataProviders.Current = new CustomModelMetadataProvider();

And now we’re going to use a new HtmlHelper called ToolTipFor.

public static IHtmlString TooltipFor<TModel, TValue>(
                             this HtmlHelper<TModel> html,
                             Expression<Func<TModel, TValue>> expression) {
    var x = ModelMetadata.FromLambdaExpression<TModel, TValue>(expression, html.ViewData);
    if (x.AdditionalValues.ContainsKey("Tooltip"))
        return new HtmlString((string)x.AdditionalValues["Tooltip"]);

    return new HtmlString("");
}

ModelMetadata.FromLambdaExpression uses our new model provider to fill out the values and we then we check for our tooltip property. If it’s found we return, else we return an empty string.

In our View we’re going to utilize our new HtmlHelper like so:

public class User {
    [Tooltip("Please enter your name.")]
    public string Name { get; set; }
}

@Html.TextBoxFor(m => m.Name, new { title=@Html.TooltipFor(m => m.Name)} )

And voila! We have a tooltip for our text input box. There are probably many more additions that can be made, such as overriding localizations. I hope this gives you a good start in how extensible MVC is.

- Ben

Kick It on DotNetKicks.com
Shout it
Digg This
Reddit This
Stumble Now!
Buzz This
Vote on DZone
Bookmark this on Delicious
Bookmark this on Technorati
Post on Twitter

  3 Responses to “Creating your own ModelMetadataProvider to handle custom attributes”

  1. Creating your own ModelMetadataProvider to handle custom attributes…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  2. [...] week or so ago I posted an article on writing your own ModelMetadataProvider. That particular method was a bit overkill for the sample [...]

  3. can i get the source code for this and here IMetaDataAware interface is missing

    thx.

Sorry, the comment form is closed at this time.

   
© 2012 BuildStarted.com Suffusion theme by Sayontan Sinha