You need to get the PropertyInfo associated with the property (e.g. via typeof(Class1).GetProperty("Name")) and then call GetCustomAttributes.
It's a bit messy due to returning multiple values - you may well want to write a helper method to do this if you need it from a few places. (There may already be a helper method in the framework somewhere, but if there is I'm unaware of it.)
using System.ComponentModel;
using System.Globalization;
using System.Linq;
public static T GetAttribute<T>(this MemberInfo member, bool isRequired)
where T : Attribute
{
var attribute = member.GetCustomAttributes(typeof(T), false).SingleOrDefault();
if (attribute == null && isRequired)
{
throw new ArgumentException(
string.Format(
CultureInfo.InvariantCulture,
"The {0} attribute must be defined on member {1}",
typeof(T).Name,
member.Name));
}
return (T)attribute;
}
public static string GetPropertyDisplayName<T>(Expression<Func<T, object>> propertyExpression)
{
var memberInfo = GetPropertyInformation(propertyExpression.Body);
if (memberInfo == null)
{
throw new ArgumentException(
"No property reference expression was found.",
"propertyExpression");
}
var attr = memberInfo.GetAttribute<DisplayNameAttribute>(false);
if (attr == null)
{
return memberInfo.Name;
}
return attr.DisplayName;
}
public static MemberInfo GetPropertyInformation(Expression propertyExpression)
{
Debug.Assert(propertyExpression != null, "propertyExpression != null");
MemberExpression memberExpr = propertyExpression as MemberExpression;
if (memberExpr == null)
{
UnaryExpression unaryExpr = propertyExpression as UnaryExpression;
if (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Convert)
{
memberExpr = unaryExpr.Operand as MemberExpression;
}
}
if (memberExpr != null && memberExpr.Member.MemberType == MemberTypes.Property)
{
return memberExpr.Member;
}
return null;
}
Nice classes by Rich Tebb!
I've been using DisplayAttribute and the code did not work for me. The only thing I've added is handling of DisplayAttribute. Brief search yielded that this attribute is new to MVC3 & .Net 4 and does almost the same thing plus more. Here's a modified version of the method:
public static string GetPropertyDisplayString<T>(Expression<Func<T, object>> propertyExpression)
{
var memberInfo = GetPropertyInformation(propertyExpression.Body);
if (memberInfo == null)
{
throw new ArgumentException(
"No property reference expression was found.",
"propertyExpression");
}
var displayAttribute = memberInfo.GetAttribute<DisplayAttribute>(false);
if (displayAttribute != null)
{
return displayAttribute.Name;
}
else
{
var displayNameAttribute = memberInfo.GetAttribute<DisplayNameAttribute>(false);
if (displayNameAttribute != null)
{
return displayNameAttribute.DisplayName;
}
else
{
return memberInfo.Name;
}
}
}
I have this generic utility method. I pass in a list of a given type (Assuming you have a supporting class) and it generates a datatable with the properties as column headers and the list items as data.
Just like in standard MVC, if you dont have DisplayName attribute defined, it will fall back to the property name so you only have to include DisplayName where it is different to the property name.
public DataTable BuildDataTable<T>(IList<T> data)
{
//Get properties
PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
//.Where(p => !p.GetGetMethod().IsVirtual && !p.GetGetMethod().IsFinal).ToArray(); //Hides virtual properties
//Get column headers
bool isDisplayNameAttributeDefined = false;
string[] headers = new string[Props.Length];
int colCount = 0;
foreach (PropertyInfo prop in Props)
{
isDisplayNameAttributeDefined = Attribute.IsDefined(prop, typeof(DisplayNameAttribute));
if (isDisplayNameAttributeDefined)
{
DisplayNameAttribute dna = (DisplayNameAttribute)Attribute.GetCustomAttribute(prop, typeof(DisplayNameAttribute));
if (dna != null)
headers[colCount] = dna.DisplayName;
}
else
headers[colCount] = prop.Name;
colCount++;
isDisplayNameAttributeDefined = false;
}
DataTable dataTable = new DataTable(typeof(T).Name);
//Add column headers to datatable
foreach (var header in headers)
dataTable.Columns.Add(header);
dataTable.Rows.Add(headers);
//Add datalist to datatable
foreach (T item in data)
{
object[] values = new object[Props.Length];
for (int col = 0; col < Props.Length; col++)
values[col] = Props[col].GetValue(item, null);
dataTable.Rows.Add(values);
}
return dataTable;
}
If there's a more efficient / safer way of doing this, I'd appreicate any feedback. The commented //Where clause will filter out virtual properties. Useful if you are using model classes directly as EF puts in "Navigation" properties as virtual. However it will also filter out any of your own virtual properties if you choose to extend such classes. For this reason, I prefer to make a ViewModel and decorate it with only the needed properties and display name attributes as required, then make a list of them.
Following Rich Tebb's and Matt Baker's answer, I wanted to use the ReflectionExtensions methods in a LINQ query, but it didn't work, so I've made this method for it to work.
If DisplayNameAttribute is set the method will return it, otherwise it will return the MemberInfo name.
public class Test
{
public Test() { }
public Test(string name, string T2)
{
Name = name;
t2 = T2;
}
[DisplayName("toto")]
public string Name { get; set; }
public string t2 { get; set; }
}
And the extension method:
public static string GetAttributeName<T>(this T itm, Expression<Func<T, object>> propertyExpression)
{
var memberInfo = GetPropertyInformation(propertyExpression.Body);
if (memberInfo == null)
{
throw new ArgumentException(
"No property reference expression was found.",
"propertyExpression");
}
var pi = typeof(T).GetProperty(memberInfo.Name);
var ret = pi.GetCustomAttributes(typeof(DisplayNameAttribute), true).Cast<DisplayNameAttribute>().SingleOrDefault();
return ret != null ? ret.DisplayName : pi.Name;
var propInfo = typeof(Class1).GetProperty("Name");
var displayNameAttribute = propInfo.GetCustomAttributes(typeof(DisplayNameAttribute), false);
var displayName = (displayNameAttribute[0] as DisplayNameAttribute).DisplayName;
displayName variable now holds the property's value.
Please try below code, I think this will solve your problem.
var classObj = new Class1();
classObj.Name => "StackOverflow";
var property = new Class1().GetType().GetProperty(nameof(classObj.Name));
var displayNameAttributeValue = (property ?? throw new InvalidOperationException())
.GetCustomAttributes(typeof(DisplayNameAttribute)) as DisplayNameAttribute;
if (displayNameAttributeValue != null)
{
Console.WriteLine("{0} = {1}", displayNameAttributeValue, classObj.Name);
}
foreach (PropertyInfo pi in properties)
{
var propName = pi.Name;
var dp = pi.GetCustomAttributes(typeof(DisplayNameAttribute), true).Cast<DisplayNameAttribute>().SingleOrDefault();
if (dp != null)
{
propName = dp.DisplayName;
}
}