So I was tired of not having any inheritance on
Attribute
s in Mvc for scaffolding, column order, display names and the rest.
Here's some code to Allow an attribute called
ParentsAttribute
to allow you to inject parental metadata for another class that Mvc's
DisplayFor
will listen to.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MyMvc.Models
{
public class ParentsAttribute:Attribute
{
public Type[] Values { get; set; }
public ParentsAttribute(params Type[] parents)
{
Values = parents;
}
}
public class MyModelMetadataProvider:DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
//called once for each property of the viewmodel
var attrs =attributes.ToArray(); // for possible multiple enumeration
var modelMetadata=base.CreateMetadata(attrs, containerType, modelAccessor, modelType, propertyName);
if (containerType == null) return modelMetadata;
var parents =(ParentsAttribute) containerType.GetCustomAttributes(typeof(ParentsAttribute),false).FirstOrDefault(); //search all parents for display attributes that we can inherit
if(parents==null)
return modelMetadata;
var props = from metdataParents in parents.Values
from mProp in metdataParents.GetProperties()
where mProp.CustomAttributes.Any() && mProp.Name == propertyName
select new { parent = metdataParents, mProp };
var sample=props.ToArray();
var q = from metdataParents in parents.Values
from mProp in metdataParents.GetProperties()
where mProp.CustomAttributes.Any() && mProp.Name == propertyName
let da = mProp.GetCustomAttributes(typeof(DisplayAttribute),true).OfType<DisplayAttribute>().FirstOrDefault()
let dna = mProp.GetCustomAttributes(typeof(DisplayNameAttribute),true).OfType<DisplayNameAttribute>().FirstOrDefault()
//don't copy down required, may not be required for the current class or viewmodel
//let ra = mProp.GetCustomAttributes(typeof(RequiredAttribute), true).OfType<RequiredAttribute>().FirstOrDefault()
where da !=null || dna !=null
select new { mProp,da,dna };
foreach (var modifier in q)
{
if (modifier.da != null)
{
//http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/1b78397f32fc#src/System.Web.Mvc/DataAnnotationsModelMetadataProvider.cs
if (string.IsNullOrEmpty(modelMetadata.Description)) modelMetadata.Description = modifier.da.GetDescription();
if (string.IsNullOrEmpty(modelMetadata.ShortDisplayName)) modelMetadata.ShortDisplayName = modifier.da.GetShortName();
if (string.IsNullOrEmpty(modelMetadata.Watermark)) modelMetadata.Watermark = modifier.da.GetPrompt();
modelMetadata.Order = modifier.da.GetOrder() ?? modelMetadata.Order;
if (string.IsNullOrEmpty(modifier.da.GetName()) == false) //only set current object if it is overriding the display
{
modelMetadata.DisplayName = modifier.da.GetName();
} else if (modifier.dna != null)
{
modelMetadata.DisplayName = modifier.dna.DisplayName;
}
}
}
return modelMetadata;
}
}
}
And don't forget to hook it up in your
Global.asax
!
ModelMetadataProviders.Current = new MyModelMetadataProvider();
No comments:
Post a Comment