I have the following method where I read from a key-value XML file. I pass in a key and am returned a value where I used to display on my view.
public static class TextManager
{
public static string GetValue(string key)
{
string returnVal = null;
XmlSerializer serializer = new XmlSerializer(typeof(Entries));
string path = HttpContext.Current.Server.MapPath("/App_Data/text-key-value.xml");
if (File.Exists(path))
{
Entries entries = (Entries)serializer.Deserialize(File.OpenRead(path));
var entry = entries.Where(u => u.Key == key).FirstOrDefault();
if (entry != null)
{
returnVal = entry.Value;
}
}
return returnVal;
}
}
Basically I want to be able to use this method in my model class as a data-annotation that will pull directly from my site text file and set to the display name property.
For instance I want to replace
[Display(Name = "Reference Code")]
public string ReferenceCode { get; set; }
With this
[DisplaySiteText("ReferenceCodeKey")]
public string ReferenceCode { get; set; }
DisplaySiteText would pass the string reference "ReferenceCodeKey" to the GetValue method, file the reference in the file and then set the standard Display name attribute to whatever was in the file.
How do I create my own custom model annotation to do this, I've written custom validation annotations in the past by creating a class that inherits from ValidationAttribute, but I don't think that will work in this case.
You can inherit DisplayNameAttribute
for this purpose
public class DisplaySiteTextAttribute : DisplayNameAttribute
{
private string _key;
public DisplaySiteTextAttribute(string key)
{
_key = key;
}
public override string DisplayName
{
get
{
return TextManager.GetValue(_key);
}
}
}
There are several options to customize model metadata:
ModelMedatadaProvider
) IMetadataAware
)The 3rd option has been discussed in the other answer. Here in this post, I'll share first and second options.
You can change the logic of getting display text without changing the attribute.
In fact it's responsibility of ModelMetaDataProvider
to get mete data for model, including display text for properties. So as an option, you can keep the Display
attribute intact and instead, create a new model metadata provider and return property metadata from a different source.
To do so, you can create a new metadata provider by deriving from DataAnnotationsModelMetadataProvider
. Then override GetMetadataForProperty
and call base, to get metadata. Then change DisplayName
based on your logic by reading from your text manager.
You also need to register the new metadata provider as ModelMetadataProviders.Current
in App_Start
.
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
public class MyCustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor,
Type containerType,
PropertyDescriptor propertyDescriptor)
{
var metadata = base.GetMetadataForProperty(modelAccessor,
containerType, propertyDescriptor);
var display = propertyDescriptor.Attributes
.OfType<DisplayAttribute>().FirstOrDefault();
if (display != null)
{
metadata.DisplayName = TextManager.GetValue(display.Name);
}
return metadata;
}
}
And then register it in Application_Start()
:
ModelMetadataProviders.Current = new MyCustomModelMetadataProvider();
For more information take a look at DataAnnotationsModelMetadataProvider.cs source code in ASP.NET MVC sources.
This approach is useful when you want to change the way that you provide metadata for model. For example when you want to load display name and description from an external file rather than resources, without changing existing attributes.
Another standard solution for creating metadata-aware attributes is creating an attribute and implementing IMetadataAware
interface. Then in implementation of OnMetadataCreated
you can easily set properties of metadata
.
This approach doesn't need to register a new meta data provider. This approach is supported by the default metadata provider and is useful for creating new metadata-aware attributes:
using System;
using System.Web.Mvc;
public class CustomMetadataAttribure : Attribute, IMetadataAware
{
public string Key { get; set; }
public CustomMetadataAttribure(string key) => this.Key = key;
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.DisplayName = TextManager.GetValue(this.Key);
}
}
This approach is useful when you want to extend metadata attributes and add a few more attributes. For example when you want to add some attributes to control rendering. You can set
ModelMetadata
properties or add some new values to itsAdditionalValues
dictionary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With