Using DisplayAttribute of column in a Controller - c#

I have defined [Display(Name = "From Date")] for columns in a Model and I am using it from a View with the help of tag helpers like asp-for and #Html.DisplayNameFor().
But I am exporting data to an Excel sheet, and that code is in a controller. Now to set the column header in excel I want to take column names from [Display(Name = "From Date")] instead of hard coding it.
How can I use it?
To make my point more clear, my existing code is:
row.CreateCell(1).SetCellValue("PCard Dt");
Now, I want to replace the hard-coded string "PCard Dt" with [Display(Name = "From Date")].
I tried various answers on Google, but it's not serving my purpose.

The key point mentioned in the comment is you need System.Reflection to extract the DisplayAttribute.
Implement an extension method to extract the value from DisplayAttribute.
public static class ReflectionExtensions
{
public static string ToName(this PropertyInfo propertyInfo)
{
try
{
object[] attributes = propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), false);
if (attributes != null && attributes.Any())
return ((DisplayAttribute)attributes[0]).Name;
return propertyInfo.Name;
}
catch
{
return propertyInfo.Name;
}
}
}
I wrote a similar implementation in GitHub.
With the below way to iterate all properties in class and get the value of DisplayAttribute.
Caller
PropertyInfo[] props = typeof(Model).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
foreach (PropertyInfo prop in props)
{
string displayName = prop.ToName();
}
Another approach to work with System.Linq.Expression.
public static class ReflectionExtensions
{
public static string ToNameByExpression<T, P>(Expression<Func<T, P>> propertyExpression) where T : new ()
{
MemberExpression expression = propertyExpression.Body as MemberExpression;
if (expression == null)
return null;
return (expression.Member as PropertyInfo)
.ToName();
}
}
Provide the expression to specify the property that you want.
Caller
string displayName = ReflectionExtensions.ToNameByExpression((Model m) => m.FromDate);
Demo # .NET Fiddle

You get display name attribute by this way:
public class TestModel
{
[DisplayName("From Date")]
public string FromDate { get; set; }
}
//generic method to get name
private string GetDisplayName<T>(string propertyName)
{
MemberInfo property = typeof(T).GetProperty(propertyName);
var attribute = property.GetCustomAttributes(typeof(DisplayNameAttribute), true)
.Cast<DisplayNameAttribute>().FirstOrDefault();
return attribute?.DisplayName ?? propertyName;
}
//get display name
var displayName = GetDisplayName<TestModel>(nameof(TestModel.FromDate));

Related

Change Display Name conditionally in View Model

Im new to Razor and View Models and I just want to ask if its possible to Display different string in [Display(Name = "")]
I tried adding condition in between the Display and the variable but it shows error
also tried this
public string Color {get;set;}
public String ColorDisplay
{
get
{
String name = "";
if (ColorId == 25 || ColorId == 26)
{
name = "Purple";
}
else
{
name = "Green";
}
return name;
}
}
Then in my View
#Html.LabelFor(m => m.ColorDisplay)
but seems not working as it just dispay ColorDisplay
In this issue, probably you may need a custom attribute to change text based on provided values inside attribute properties. Assumed that you want custom attribute usage like this:
[DisplayWhen("ColorId", 25, 26, "Purple", "Green")]
public String Color { get; set; }
And using HTML helper like this:
#Html.LabelFor(m => m.Color)
Then you should do these steps:
1) Create a custom attribute inherited from Attribute class.
public class DisplayWhenAttribute : Attribute
{
private string _propertyName;
private int _condition1;
private int _condition2;
private string _trueValue;
private string _falseValue;
public string PropertyName
{
get
{
return _propertyName;
}
}
public int Condition1
{
get
{
return _condition1;
}
}
public int Condition2
{
get
{
return _condition2;
}
}
public string TrueValue
{
get
{
return _trueValue;
}
}
public string FalseValue
{
get
{
return _falseValue;
}
}
public DisplayWhenAttribute(string propertyName, int condition1, int condition2, string trueValue, string falseValue)
{
_propertyName = propertyName;
_condition1 = condition1;
_condition2 = condition2;
_trueValue = trueValue;
_falseValue = falseValue;
}
}
2) Create custom metadata provider class which checks existence of custom attribute.
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
var additionalAttribute = attributes.OfType<DisplayWhenAttribute>().FirstOrDefault();
if (additionalAttribute != null)
{
metadata.AdditionalValues.Add("DisplayWhenAttribute", additionalValues);
}
return metadata;
}
}
3) Register CustomModelMetadataProvider into Application_Start() method inside Global.asax like this:
protected void Application_Start()
{
ModelMetadataProviders.Current = new CustomModelMetadataProvider();
}
4) Create your own (or override existing) LabelFor helper so that it checks against DisplayWhenAttribute, as in example below:
public static MvcHtmlString LabelFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
{
string result = string.Empty;
var modelMetaData = expression.Compile().Invoke(helper.ViewData.Model);
string fieldName = ExpressionHelper.GetExpressionText(expression);
var containerType = typeof(TModel);
var containerProperties = containerType.GetProperties();
var propertyInfo = containerProperties.SingleOrDefault(x => x.Name == modelMetaData.PropertyName);
var attribute = propertyInfo.GetCustomAttributes(false).SingleOrDefault(x => x is DisplayWhenAttribute) as DisplayWhenAttribute;
var target = attribute.PropertyName; // target property name, e.g. ColorId
var condition1 = attribute.Condition1; // first value to check
var condition2 = attribute.Condition2; // second value to check
var targetValue = (int)containerType.GetProperty(target).GetValue(helper.ViewData.Model);
// checking provided values from attribute
if (targetValue == condition1 || targetValue == condition2)
{
result = attribute.TrueValue;
}
else
{
result = attribute.FalseValue;
}
// create <label> tag with specified true/false value
TagBuilder tag = new TagBuilder("label");
tag.MergeAttributes(htmlAttributes);
tag.Attributes.Add("for", helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(fieldName));
tag.SetInnerText(result);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
Some references to consider with:
Is it possible to create conditional attribute as DisplayIf?
How to extend MVC3 Label and LabelFor HTML helpers?
MVC custom display attribute

Write extension method to retrieve property description using c#

I have following class
public class Device
{
[XmlElement("MobileDeviceType")]
public string DeviceType { get; set; }
}
I need extension method called "GetXElementName()" and I need to use the method like below.
string propertyDescription = (new Device()).DeviceType.GetXElementName(); // this shoud return "MobileDeviceType"
As a example
public static class ExtensionMethods
{
public static string GetXElementName<T>(this T source)
{
PropertyInfo prop = source.GetType().GetProperty(source.ToString());
string desc = prop.Name;
object[] attrs = prop.GetCustomAttributes(true);
object attr = attrs[0];
XmlElementAttribute descAttr = attr as XmlElementAttribute;
if (descAttr != null)
{
desc = descAttr.ElementName;
}
return desc;
}
}
Can I know how should I modify the method body to use the "GetXElementName()" method to use like I explained above.
You need to use Expressions to achieve that, because you need to know the member, not the value.
public static class Extensions
{
public static string GetXmlElementName<T, TProperty>(this T obj, Expression<Func<T, TProperty>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
return string.Empty;
var xmlElementAttribute = memberExpression.Member.GetCustomAttribute<XmlElementAttribute>();
if (xmlElementAttribute == null)
return string.Empty;
return xmlElementAttribute.ElementName;
}
}
Usage:
public class MyClass
{
[XmlElement(ElementName = "Test")]
public string MyProperty { get; set; }
}
new MyClass().GetXmlElementName(x => x.MyProperty) // output "Test"
EDIT: another version, without an object instance (see Nyerguds comment)
I guess the most elegant way is make a generic class, with a generic method, so you can call it by specify only the T type parameter (TProperty is taken implicitly).
public class GetXmlElementName<T>
{
public static string From<TProperty>(Expression<Func<T, TProperty>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
return string.Empty;
var xmlElementAttribute = memberExpression.Member.GetCustomAttribute<XmlElementAttribute>();
if (xmlElementAttribute == null)
return string.Empty;
return xmlElementAttribute.ElementName;
}
}
Usage:
GetXmlElementName<MyClass>.From(x => x.MyProperty) // output "Test"

Can I tell MVC to separate pascal-cased properties in to words automatically?

I often have C# code like
[DisplayName("Number of Questions")]
public int NumberOfQuestions { get; set; }
Where I use the DisplayName property to add in spaces when it is displayed. Is there an option to tell MVC to add spaces by default if the DisplayName annotation is not explicitly provided?
Thanks
There are two ways.
1.Override LabelFor and creating a custom HTML helper:
Utility class for Custom HTML helper:
public class CustomHTMLHelperUtilities
{
// Method to Get the Property Name
internal static string PropertyName<T, TResult>(Expression<Func<T, TResult>> expression)
{
switch (expression.Body.NodeType)
{
case ExpressionType.MemberAccess:
var memberExpression = expression.Body as MemberExpression;
return memberExpression.Member.Name;
default:
return string.Empty;
}
}
// Method to split the camel case
internal static string SplitCamelCase(string camelCaseString)
{
string output = System.Text.RegularExpressions.Regex.Replace(
camelCaseString,
"([A-Z])",
" $1",
RegexOptions.Compiled).Trim();
return output;
}
}
Custom Helper:
public static class LabelHelpers
{
public static MvcHtmlString LabelForCamelCase<T, TResult>(this HtmlHelper<T> helper, Expression<Func<T, TResult>> expression, object htmlAttributes = null)
{
string propertyName = CustomHTMLHelperUtilities.PropertyName(expression);
string labelValue = CustomHTMLHelperUtilities.SplitCamelCase(propertyName);
#region Html attributes creation
var builder = new TagBuilder("label ");
builder.Attributes.Add("text", labelValue);
builder.Attributes.Add("for", propertyName);
#endregion
#region additional html attributes
if (htmlAttributes != null)
{
var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
builder.MergeAttributes(attributes);
}
#endregion
MvcHtmlString retHtml = new MvcHtmlString(builder.ToString(TagRenderMode.SelfClosing));
return retHtml;
}
}
Use in CSHTML:
#Html.LabelForCamelCase(m=>m.YourPropertyName, new { style="color:red"})
Your label will display as 'Your Property Name'
2.Using Resource File:
[Display(Name = "PropertyKeyAsperResourceFile", ResourceType = typeof(ResourceFileName))]
public string myProperty { get; set; }
I will prefer the first solution. Because a resource file is intend to do a separate and reserved role in a project. Additionally the custom HTML helper can be reused once created.

Need Enum's Display Names as a List [duplicate]

I've got a property in my model called Promotion that its type is a flag enum called UserPromotion. Members of my enum have display attributes set as follows:
[Flags]
public enum UserPromotion
{
None = 0x0,
[Display(Name = "Send Job Offers By Mail")]
SendJobOffersByMail = 0x1,
[Display(Name = "Send Job Offers By Sms")]
SendJobOffersBySms = 0x2,
[Display(Name = "Send Other Stuff By Sms")]
SendPromotionalBySms = 0x4,
[Display(Name = "Send Other Stuff By Mail")]
SendPromotionalByMail = 0x8
}
Now I want to be able to create say a ul in my view to show the selected values of my Promotion property. This is what I have done so far but the problem is that how can I get the display names here?
<ul>
#foreach (int aPromotion in #Enum.GetValues(typeof(UserPromotion)))
{
var currentPromotion = (int)Model.JobSeeker.Promotion;
if ((currentPromotion & aPromotion) == aPromotion)
{
<li>Here I don't know how to get the display attribute of "currentPromotion".</li>
}
}
</ul>
One liner - Fluent syntax
public static class Extensions
{
/// <summary>
/// A generic extension method that aids in reflecting
/// and retrieving any attribute that is applied to an `Enum`.
/// </summary>
public static TAttribute GetAttribute<TAttribute>(this Enum enumValue)
where TAttribute : Attribute
{
return enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<TAttribute>();
}
}
Example
public enum Season
{
[Display(Name = "It's autumn")]
Autumn,
[Display(Name = "It's winter")]
Winter,
[Display(Name = "It's spring")]
Spring,
[Display(Name = "It's summer")]
Summer
}
public class Foo
{
public Season Season = Season.Summer;
public void DisplayName()
{
var seasonDisplayName = Season.GetAttribute<DisplayAttribute>();
Console.WriteLine("Which season is it?");
Console.WriteLine (seasonDisplayName.Name);
}
}
Output
Which season is it?
It's summer
UPDATE
First solution was focused on getting display names from enum. Code below should be exact solution for your problem.
You can use this helper class for enums:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static class EnumHelper<T>
where T : struct, Enum // This constraint requires C# 7.3 or later.
{
public static IList<T> GetValues(Enum value)
{
var enumValues = new List<T>();
foreach (FieldInfo fi in value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public))
{
enumValues.Add((T)Enum.Parse(value.GetType(), fi.Name, false));
}
return enumValues;
}
public static T Parse(string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
public static IList<string> GetNames(Enum value)
{
return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList();
}
public static IList<string> GetDisplayValues(Enum value)
{
return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList();
}
private static string lookupResource(Type resourceManagerProvider, string resourceKey)
{
var resourceKeyProperty = resourceManagerProvider.GetProperty(resourceKey,
BindingFlags.Static | BindingFlags.Public, null, typeof(string),
new Type[0], null);
if (resourceKeyProperty != null)
{
return (string)resourceKeyProperty.GetMethod.Invoke(null, null);
}
return resourceKey; // Fallback with the key name
}
public static string GetDisplayValue(T value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var descriptionAttributes = fieldInfo.GetCustomAttributes(
typeof(DisplayAttribute), false) as DisplayAttribute[];
if (descriptionAttributes[0].ResourceType != null)
return lookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name);
if (descriptionAttributes == null) return string.Empty;
return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString();
}
}
And then you can use it in your view as following:
<ul>
#foreach (var value in #EnumHelper<UserPromotion>.GetValues(UserPromotion.None))
{
if (value == Model.JobSeeker.Promotion)
{
var description = EnumHelper<UserPromotion>.GetDisplayValue(value);
<li>#Html.DisplayFor(e => description )</li>
}
}
</ul>
Building on Aydin's great answer, here's an extension method that doesn't require any type parameters.
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static class EnumExtensions
{
public static string GetDisplayName(this Enum enumValue)
{
return enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
.GetName();
}
}
NOTE: GetName() should be used instead of the Name property. This ensures that the localized string will be returned if using the ResourceType attribute property.
Example
To use it, just reference the enum value in your view.
#{
UserPromotion promo = UserPromotion.SendJobOffersByMail;
}
Promotion: #promo.GetDisplayName()
Output
Promotion: Send Job Offers By Mail
Based on Aydin's answer I would suggest a less "duplicatious" implementation (because we could easily get the Type from the Enum value itself, instead of providing it as a parameter 馃槈:
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static string GetDisplayName(this Enum enumValue)
{
return enumValue.GetType().GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
.Name;
}
EDIT (based upon #Vahagn Nahapetyan's comment)
public static string GetDisplayName(this Enum enumValue)
{
return enumValue.GetType()?
.GetMember(enumValue.ToString())?
.First()?
.GetCustomAttribute<DisplayAttribute>()?
.Name;
}
Now we can use it very clean in this way:
public enum Season
{
[Display(Name = "The Autumn")]
Autumn,
[Display(Name = "The Weather")]
Winter,
[Display(Name = "The Tease")]
Spring,
[Display(Name = "The Dream")]
Summer
}
Season.Summer.GetDisplayName();
Which results in
"The Dream"
If you are using MVC 5.1 or upper there is simplier and clearer way: just use data annotation (from System.ComponentModel.DataAnnotations namespace) like below:
public enum Color
{
[Display(Name = "Dark red")]
DarkRed,
[Display(Name = "Very dark red")]
VeryDarkRed,
[Display(Name = "Red or just black?")]
ReallyDarkRed
}
And in view, just put it into proper html helper:
#Html.EnumDropDownListFor(model => model.Color)
Building on Todd's great answer which built on Aydin's great answer, here's a generic extension method which doesn't require any type parameters.
/// <summary>
/// Gets human-readable version of enum.
/// </summary>
/// <returns>effective DisplayAttribute.Name of given enum.</returns>
public static string GetDisplayName<T>(this T enumValue) where T : IComparable, IFormattable, IConvertible // C# 7.3+: where T : struct, Enum
{
if (!typeof(T).IsEnum) // Not needed in C# 7.3+ with above updated constraint
throw new ArgumentException("Argument must be of type Enum");
DisplayAttribute displayAttribute = enumValue.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
string displayName = displayAttribute?.GetName();
return displayName ?? enumValue.ToString();
}
I needed this for my project because something like the below code, where not every member of the enum has a DisplayAttribute, throws an exception with Todd's solution:
public class MyClass
{
public enum MyEnum
{
[Display(Name="ONE")]
One,
// No DisplayAttribute
Two
}
public void UseMyEnum()
{
MyEnum foo = MyEnum.One;
MyEnum bar = MyEnum.Two;
Console.WriteLine(foo.GetDisplayName());
Console.WriteLine(bar.GetDisplayName());
}
}
// Output:
//
// ONE
// Two
If this is a complicated solution to a simple problem, please let me know, but this was the fix I used.
You could use Type.GetMember Method, then get the attribute info using reflection:
// display attribute of "currentPromotion"
var type = typeof(UserPromotion);
var memberInfo = type.GetMember(currentPromotion.ToString());
var attributes = memberInfo[0].GetCustomAttributes(typeof(DisplayAttribute), false);
var description = ((DisplayAttribute)attributes[0]).Name;
There were a few similar posts here:
Getting attributes of Enum's value
How to make MVC3 DisplayFor show the value of an Enum's Display-Attribute?
In .NET5, I used DisplayTextFor without needing helper or extension methods:
#Html.DisplayTextFor(m => m.SomeEnumProperty)
Where SomeEnumProperty has a value of:
public enum MyEnum
{
[Display(Name = "Not started")]
NotStarted = 0,
[Display(Name = "Weird display name instead of just 'Started'")]
Started = 1,
}
For ASP.Net Core 3.0, this worked for me (credit to previous answerers).
My Enum class:
using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
public class Enums
{
public enum Duration
{
[Display(Name = "1 Hour")]
OneHour,
[Display(Name = "1 Day")]
OneDay
}
// Helper method to display the name of the enum values.
public static string GetDisplayName(Enum value)
{
return value.GetType()?
.GetMember(value.ToString())?.First()?
.GetCustomAttribute<DisplayAttribute>()?
.Name;
}
}
My View Model Class:
public class MyViewModel
{
public Duration Duration { get; set; }
}
An example of a razor view displaying a label and a drop-down list. Notice the drop-down list does not require a helper method:
#model IEnumerable<MyViewModel>
#foreach (var item in Model)
{
<label asp-for="#item.Duration">#Enums.GetDisplayName(item.Duration)</label>
<div class="form-group">
<label asp-for="#item.Duration" class="control-label">Select Duration</label>
<select asp-for="#item.Duration" class="form-control"
asp-items="Html.GetEnumSelectList<Enums.Duration>()">
</select>
</div>
}
With Core 2.1,
public static string GetDisplayName(Enum enumValue)
{
return enumValue.GetType()?
.GetMember(enumValue.ToString())?[0]?
.GetCustomAttribute<DisplayAttribute>()?
.Name;
}
<ul>
#foreach (int aPromotion in #Enum.GetValues(typeof(UserPromotion)))
{
var currentPromotion = (int)Model.JobSeeker.Promotion;
if ((currentPromotion & aPromotion) == aPromotion)
{
<li>#Html.DisplayFor(e => currentPromotion)</li>
}
}
</ul>
combining all edge-cases together from above:
enum members with base object members' names (Equals, ToString)
optional Display attribute
here is my code:
public enum Enum
{
[Display(Name = "What a weird name!")]
ToString,
Equals
}
public static class EnumHelpers
{
public static string GetDisplayName(this Enum enumValue)
{
var enumType = enumValue.GetType();
return enumType
.GetMember(enumValue.ToString())
.Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == enumType)
.First()
.GetCustomAttribute<DisplayAttribute>()?.Name ?? enumValue.ToString();
}
}
void Main()
{
Assert.Equals("What a weird name!", Enum.ToString.GetDisplayName());
Assert.Equals("Equals", Enum.Equals.GetDisplayName());
}
You need to use a bit of reflection in order to access that attribute:
var type = typeof(UserPromotion);
var member = type.GetMember(Model.JobSeeker.Promotion.ToString());
var attributes = member[0].GetCustomAttributes(typeof(DisplayAttribute), false);
var name = ((DisplayAttribute)attributes[0]).Name;
I recommend wrapping this method in a extension method or perform this in a view model.
I'm sorry to do this, but I couldn't use any of the other answers as-is and haven't time to duke it out in the comments.
Uses C# 6 syntax.
static class EnumExtensions
{
/// returns the localized Name, if a [Display(Name="Localised Name")] attribute is applied to the enum member
/// returns null if there isnt an attribute
public static string DisplayNameOrEnumName(this Enum value)
// => value.DisplayNameOrDefault() ?? value.ToString()
{
// More efficient form of ^ based on http://stackoverflow.com/a/17034624/11635
var enumType = value.GetType();
var enumMemberName = Enum.GetName(enumType, value);
return enumType
.GetEnumMemberAttribute<DisplayAttribute>(enumMemberName)
?.GetName() // Potentially localized
?? enumMemberName; // Or fall back to the enum name
}
/// returns the localized Name, if a [Display] attribute is applied to the enum member
/// returns null if there is no attribute
public static string DisplayNameOrDefault(this Enum value) =>
value.GetEnumMemberAttribute<DisplayAttribute>()?.GetName();
static TAttribute GetEnumMemberAttribute<TAttribute>(this Enum value) where TAttribute : Attribute =>
value.GetType().GetEnumMemberAttribute<TAttribute>(value.ToString());
static TAttribute GetEnumMemberAttribute<TAttribute>(this Type enumType, string enumMemberName) where TAttribute : Attribute =>
enumType.GetMember(enumMemberName).Single().GetCustomAttribute<TAttribute>();
}
Building further on Aydin's and Todd's answers, here is an extension method that also lets you get the name from a resource file
using AppResources;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Resources;
public static class EnumExtensions
{
public static string GetDisplayName(this Enum enumValue)
{
var enumMember= enumValue.GetType()
.GetMember(enumValue.ToString());
DisplayAttribute displayAttrib = null;
if (enumMember.Any()) {
displayAttrib = enumMember
.First()
.GetCustomAttribute<DisplayAttribute>();
}
string name = null;
Type resource = null;
if (displayAttrib != null)
{
name = displayAttrib.Name;
resource = displayAttrib.ResourceType;
}
return String.IsNullOrEmpty(name) ? enumValue.ToString()
: resource == null ? name
: new ResourceManager(resource).GetString(name);
}
}
and use it like
public enum Season
{
[Display(ResourceType = typeof(Resource), Name = Season_Summer")]
Summer
}
For just displaying enum's display name attribute just use
Microsoft.AspNetCore.Mvc.Rendering's
#Html.DisplayFor(x => EnumType.EnumValue)
That's would be enough.
For displaying SelectList write as following:
<select id="someIdForTheEndPoint" asp-items="Html.GetEnumSelectList<EnumType>()">
<option selected="selected" value="">Select value</option>
</select>
I have two solutions for this Question.
The first solution is on getting display names from enum.
public enum CourseLocationTypes
{
[Display(Name = "On Campus")]
OnCampus,
[Display(Name = "Online")]
Online,
[Display(Name = "Both")]
Both
}
public static string DisplayName(this Enum value)
{
Type enumType = value.GetType();
string enumValue = Enum.GetName(enumType, value);
MemberInfo member = enumType.GetMember(enumValue)[0];
object[] attrs = member.GetCustomAttributes(typeof(DisplayAttribute), false);
string outString = ((DisplayAttribute)attrs[0]).Name;
if (((DisplayAttribute)attrs[0]).ResourceType != null)
{
outString = ((DisplayAttribute)attrs[0]).GetName();
}
return outString;
}
<h3 class="product-title white">#Model.CourseLocationType.DisplayName()</h3>
The second Solution is on getting display name from enum name but that will be enum split in developer language it's called patch.
public static string SplitOnCapitals(this string text)
{
var r = new Regex(#"
(?<=[A-Z])(?=[A-Z][a-z]) |
(?<=[^A-Z])(?=[A-Z]) |
(?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);
return r.Replace(text, " ");
}
<div class="widget-box pt-0">
#foreach (var item in Enum.GetNames(typeof(CourseLocationType)))
{
<label class="pr-2 pt-1">
#Html.RadioButtonFor(x => x.CourseLocationType, item, new { type = "radio", #class = "iCheckBox control-label" }) #item.SplitOnCapitals()
</label>
}
#Html.ValidationMessageFor(x => x.CourseLocationType)
</div>
2020 Update: An updated version of the function provided by many in this thread but now for C# 7.3 onwards:
Now you can restrict generic methods to enums types so you can write a single method extension to use it with all your enums like this:
The generic extension method:
public static string ATexto<T>(this T enumeraci贸n) where T : struct, Enum {
var tipo = enumeraci贸n.GetType();
return tipo.GetMember(enumeraci贸n.ToString())
.Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == tipo).First()
.GetCustomAttribute<DisplayAttribute>()?.Name ?? enumeraci贸n.ToString();
}
The enum:
public enum TipoImpuesto {
IVA, INC, [Display(Name = "IVA e INC")]IVAeINC, [Display(Name = "No aplica")]NoAplica };
How to use it:
var tipoImpuesto = TipoImpuesto.IVAeINC;
var textoTipoImpuesto = tipoImpuesto.ATexto(); // Prints "IVA e INC".
Bonus, Enums with Flags: If you are dealing with normal enums the function above is enough, but if any of your enums can take multiple values with the use of flags then you will need to modify it like this (This code uses C#8 features):
public static string ATexto<T>(this T enumeraci贸n) where T : struct, Enum {
var tipo = enumeraci贸n.GetType();
var textoDirecto = enumeraci贸n.ToString();
string obtenerTexto(string textoDirecto) => tipo.GetMember(textoDirecto)
.Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == tipo)
.First().GetCustomAttribute<DisplayAttribute>()?.Name ?? textoDirecto;
if (textoDirecto.Contains(", ")) {
var texto = new StringBuilder();
foreach (var textoDirectoAux in textoDirecto.Split(", ")) {
texto.Append($"{obtenerTexto(textoDirectoAux)}, ");
}
return texto.ToString()[0..^2];
} else {
return obtenerTexto(textoDirecto);
}
}
The enum with flags:
[Flags] public enum TipoContribuyente {
[Display(Name = "Com煤n")] Com煤n = 1,
[Display(Name = "Gran Contribuyente")] GranContribuyente = 2,
Autorretenedor = 4,
[Display(Name = "Retenedor de IVA")] RetenedorIVA = 8,
[Display(Name = "R茅gimen Simple")] R茅gimenSimple = 16 }
How to use it:
var tipoContribuyente = TipoContribuyente.RetenedorIVA | TipoContribuyente.GranContribuyente;
var textoAux = tipoContribuyente.ATexto(); // Prints "Gran Contribuyente, Retenedor de IVA".
I want to contribute with culture-dependent GetDisplayName enum extension. Hope this will be usefull for anyone googling this answer like me previously:
"standart" way as Aydin Adn and Todd mentioned:
public static string GetDisplayName(this Enum enumValue)
{
return enumValue
.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>()
.GetName();
}
"Culture-dependent" way:
public static string GetDisplayName(this Enum enumValue, CultureInfo ci)
{
var displayAttr = enumValue
.GetType()
.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
var resMan = displayAttr.ResourceType?.GetProperty(#"ResourceManager", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null, null) as ResourceManager;
return resMan?.GetString(displayAttr.Name, ci) ?? displayAttr.GetName();
}
It is maybe cheating, but it's works:
#foreach (var yourEnum in Html.GetEnumSelectList<YourEnum>())
{
#yourEnum.Text
}
Based on previous answers I've created this comfortable helper to support all DisplayAttribute properties in a readable way:
public static class EnumExtensions
{
public static DisplayAttributeValues GetDisplayAttributeValues(this Enum enumValue)
{
var displayAttribute = enumValue.GetType().GetMember(enumValue.ToString()).First().GetCustomAttribute<DisplayAttribute>();
return new DisplayAttributeValues(enumValue, displayAttribute);
}
public sealed class DisplayAttributeValues
{
private readonly Enum enumValue;
private readonly DisplayAttribute displayAttribute;
public DisplayAttributeValues(Enum enumValue, DisplayAttribute displayAttribute)
{
this.enumValue = enumValue;
this.displayAttribute = displayAttribute;
}
public bool? AutoGenerateField => this.displayAttribute?.GetAutoGenerateField();
public bool? AutoGenerateFilter => this.displayAttribute?.GetAutoGenerateFilter();
public int? Order => this.displayAttribute?.GetOrder();
public string Description => this.displayAttribute != null ? this.displayAttribute.GetDescription() : string.Empty;
public string GroupName => this.displayAttribute != null ? this.displayAttribute.GetGroupName() : string.Empty;
public string Name => this.displayAttribute != null ? this.displayAttribute.GetName() : this.enumValue.ToString();
public string Prompt => this.displayAttribute != null ? this.displayAttribute.GetPrompt() : string.Empty;
public string ShortName => this.displayAttribute != null ? this.displayAttribute.GetShortName() : this.enumValue.ToString();
}
}
I tried doing this as an edit but it was rejected; I can't see why.
The above will throw an exception if you call it with an Enum that has a mix of custom attributes and plain items, e.g.
public enum CommentType
{
All = 1,
Rent = 2,
Insurance = 3,
[Display(Name="Service Charge")]
ServiceCharge = 4
}
So I've modified the code ever so slightly to check for custom attributes before trying to access them, and use the name if none are found.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
public static class EnumHelper<T>
{
public static IList<T> GetValues(Enum value)
{
var enumValues = new List<T>();
foreach (FieldInfo fi in value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public))
{
enumValues.Add((T)Enum.Parse(value.GetType(), fi.Name, false));
}
return enumValues;
}
public static T Parse(string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
public static IList<string> GetNames(Enum value)
{
return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList();
}
public static IList<string> GetDisplayValues(Enum value)
{
return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList();
}
private static string lookupResource(Type resourceManagerProvider, string resourceKey)
{
foreach (PropertyInfo staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
{
System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
return resourceManager.GetString(resourceKey);
}
}
return resourceKey; // Fallback with the key name
}
public static string GetDisplayValue(T value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var descriptionAttributes = fieldInfo.GetCustomAttributes(
typeof(DisplayAttribute), false) as DisplayAttribute[];
if (descriptionAttributes.Any() && descriptionAttributes[0].ResourceType != null)
return lookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name);
if (descriptionAttributes == null) return string.Empty;
return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString();
}
}
Using MVC5 you could use:
public enum UserPromotion
{
None = 0x0,
[Display(Name = "Send Job Offers By Mail")]
SendJobOffersByMail = 0x1,
[Display(Name = "Send Job Offers By Sms")]
SendJobOffersBySms = 0x2,
[Display(Name = "Send Other Stuff By Sms")]
SendPromotionalBySms = 0x4,
[Display(Name = "Send Other Stuff By Mail")]
SendPromotionalByMail = 0x8
}
then if you want to create a dropdown selector you can use:
#Html.EnumDropdownListFor(expression: model => model.PromotionSelector, optionLabel: "Select")
assume that your enum name is OrderState, Use this code:
#Html.DropDownList("selectList", new SelectList(Html.GetEnumSelectList<OrderState>(), "Value", "Text",ViewBag.selectedOrderState), new {#id="OrderState", #class = "form-control" })
and set selected option in backend:
var selectedOrderState = ..Data.OrderState.GetHashCode();
ViewBag.selectedOrderState = selectedOrderState;

Getting the owning object of a property from a Property Expression

I'm working on a bit of code which has an end purpose of letting you use a property expression to set the value of a property with similar syntax to passing a variable as an out or ref parameter.
Something along the lines of:
public static foo(()=>Object.property, value);
And Object.Property will be assigned the value of value.
I'm using the following code to get the owining object of the property:
public static object GetOwningObject<T>(this Expression<Func<T>> #this)
{
var memberExpression = #this.Body as MemberExpression;
if (memberExpression != null)
{
var fieldExpression = memberExpression.Expression as MemberExpression;
if (fieldExpression != null)
{
var constExpression = fieldExpression.Expression as ConstantExpression;
var field = fieldExpression.Member as FieldInfo;
if (constExpression != null) if (field != null) return field.GetValue(constExpression.Value);
}
}
return null;
}
So this would, when used on a property expression like ()=>Object.Property, give back the instance of 'Object'. I'm somewhat new to using property expressions, and there seems to be many different ways to accomplish things, but I want to extend what I have so far, so that given an expression such as ()=>Foo.Bar.Baz it will give the Bar, not Foo. I always want the last containing object in the expression.
Any ideas? Thanks in advance.
What you have to do is to traverse through property chain to the most outer object.
The sample below is rather self explanatory and shows that the extension method would work for chained fields as well as properties:
class Foo
{
public Bar Bar { get; set; }
}
class Bar
{
public string Baz { get; set; }
}
class FooWithField
{
public BarWithField BarField;
}
class BarWithField
{
public string BazField;
}
public static class LambdaExtensions
{
public static object GetRootObject<T>(this Expression<Func<T>> expression)
{
var propertyAccessExpression = expression.Body as MemberExpression;
if (propertyAccessExpression == null)
return null;
//go up through property/field chain
while (propertyAccessExpression.Expression is MemberExpression)
propertyAccessExpression = (MemberExpression)propertyAccessExpression.Expression;
//the last expression suppose to be a constant expression referring to captured variable ...
var rootObjectConstantExpression = propertyAccessExpression.Expression as ConstantExpression;
if (rootObjectConstantExpression == null)
return null;
//... which is stored in a field of generated class that holds all captured variables.
var fieldInfo = propertyAccessExpression.Member as FieldInfo;
if (fieldInfo != null)
return fieldInfo.GetValue(rootObjectConstantExpression.Value);
return null;
}
}
[TestFixture]
public class Program
{
[Test]
public void Should_find_root_element_by_property_chain()
{
var foo = new Foo { Bar = new Bar { Baz = "text" } };
Expression<Func<string>> expression = () => foo.Bar.Baz;
Assert.That(expression.GetRootObject(), Is.SameAs(foo));
}
[Test]
public void Should_find_root_element_by_field_chain()
{
var foo = new FooWithField { BarField = new BarWithField { BazField = "text" } };
Expression<Func<string>> expression = () => foo.BarField.BazField;
Assert.That(expression.GetRootObject(), Is.SameAs(foo));
}
}
If your project is an MVC 5 project and you have a reference to the assembly System.Web.Mvc You could use the following:
Some time ago I wrote an extension method to easily create a multiselect dropdown (based on bootstrap 4) and it looked something like this:
public static MvcHtmlString MultiSelectFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
/*The challenge I faced here was that the expression you passed could very well be nested, so in order overcome this, I decompiled the dll to see how MVC does it, and I found this piece of code.*/
string expressionText = System.Web.Mvc.ExpressionHelper.GetExpressionText((LambdaExpression)expression);
System.Web.Mvc.ModelMetadata metadata = System.Web.Mvc.ModelMetadata.FromStringExpression(expressionText, htmlHelper.ViewData);
}
The metadata object has a Property called PropertyName and another property called Container which is a reference to the instance of the container object.

Categories

Resources