Using the scaffolding in MVC makes it easy to knock up CRUD applications without too much difficulty. Typically you end up with something like this – note the PascalCase field names taken straight from the model generated by Html.LabelFor
:
So now the obvious thing is to add DisplayName
attributes metadata to your model, for example:
[DisplayName("Assigned To")] public int AssignedToId { get; set; } [DisplayName("Customer Name")] public string CustomerName { get; set; }
Meh, donkey work. In most cases it just needs better default labels in the absence of explicit DisplayName annotations. Nine times out of ten field names such as AssignedToId and CustomerName would be simply expanded to Assigned To and Customer Name. (Of course, they don’t do this out of the box because these simple rules wouldn’t hold true for all languages.)
In MVC you can hook into the metadata discovery/generation process by implementing a ModelMetadataProvider
, the default one of which is the DataAnnotationsModelMetadataProvider
. So all I did was inherit from the latter and override the GetMetadataForProperty
method and if no DisplayName has been specified on the model, create one based on the model’s camel-cased property name.
class MyFabulousModelMetadataProvider : DataAnnotationsModelMetadataProvider { // Uppercase followed by lowercase but not on existing word boundary (eg. the start) Regex _camelCaseRegex = new Regex(@"Bp{Lu}p{Ll}", RegexOptions.Compiled); // Creates a nice DisplayName from the model’s property name if one hasn't been specified protected override ModelMetadata GetMetadataForProperty( Func<object> modelAccessor, Type containerType, PropertyDescriptor propertyDescriptor) { ModelMetadata metadata = base.GetMetadataForProperty(modelAccessor, containerType, propertyDescriptor); if (metadata.DisplayName == null) metadata.DisplayName = displayNameFromCamelCase(metadata.GetDisplayName()); return metadata; } string displayNameFromCamelCase(string name) { name = _camelCaseRegex.Replace(name, " $0"); if (name.EndsWith(" Id")) name = name.Substring(0, name.Length - 3); return name; } }
Hook the provider up in Application_Start
by assigning an instance of it to ModelMetadataProviders.Current
.
It’s pretty effective I think:
Anything that isn’t quite right can be overridden simply my explicitly adding DisplayNames to the model.
One teeny problem – the code line with ‘Func modelAccessor’ should read ‘Func modelAccessor’.
But that is a minor quibble. AWESOME work fella!
Good catch James – I’ll see if I can stop WP being so “helpful”
Gah! WordPress must have stripped the angle brackets out of your code just like it did my comment!
Should read: Func<object> modelAccessor