Get [Display] attribute value from resource in MVC - asp.net-mvc

I have used the [Display] attribute for one of my enums:
public enum eCommentType
{
[Display(ResourceType = typeof(FaultManagementStrings), Name = "NormalComment")]
NormalComment = 0,
[Display(ResourceType = typeof(FaultManagementStrings), Name = "OpenningComment")]
OpenningComment = 1,
[Display(ResourceType = typeof(FaultManagementStrings), Name = "StartProgressComment")]
StartProgressComment = 2,
[Display(ResourceType = typeof(FaultManagementStrings), Name = "ClouserComment")]
ClouserComment = 3,
[Display(ResourceType = typeof(FaultManagementStrings), Name = "ReopennignComment")]
ReopennignComment = 4
}
Is it possible to create an Extention method that will reuse the exsisting MVC finctionallity of getting the Display attribute value from the specified resource ?
I would what something like that...
#Html.GetEnumDisplayAttributeValue(c=> comment.CommentType)
I know i could wirte something that will implement the required reflection and find the value of the resource type and the call resource manager and so on... but i think that maybe it is possible to use the exsisting built in functionally of mvc.. after all it is already done when you call a LabelFor helper.
is is possible or should i reinvent the wheel ?

I have had the same problem and created these extension methods:
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Mvc.Html;
public static class EnumHelper
{
private static readonly SelectListItem[] SingleEmptyItem = new[] { new SelectListItem { Text = string.Empty, Value = string.Empty } };
public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)
{
return htmlHelper.EnumDropDownListFor(expression, null);
}
public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var enumType = GetNonNullableModelType(metadata);
var values = Enum.GetValues(enumType).Cast<TEnum>();
var items =
values.Select(value => new SelectListItem
{
Text = GetName(value),
Value = value.ToString(),
Selected = value.Equals(metadata.Model)
});
if (metadata.IsNullableValueType)
{
items = SingleEmptyItem.Concat(items);
}
return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
}
private static string GetName<TEnum>(TEnum value)
{
var displayAttribute = GetDisplayAttribute(value);
return displayAttribute == null ? value.ToString() : displayAttribute.GetName();
}
private static DisplayAttribute GetDisplayAttribute<TEnum>(TEnum value)
{
return value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(typeof(DisplayAttribute), false)
.Cast<DisplayAttribute>()
.FirstOrDefault();
}
private static Type GetNonNullableModelType(ModelMetadata modelMetadata)
{
var realModelType = modelMetadata.ModelType;
var underlyingType = Nullable.GetUnderlyingType(realModelType);
return underlyingType ?? realModelType;
}
}
You can use these extension methods in your views as follows:
#Html.EnumDropDownListFor(c=> comment.CommentType)
You should now see a dropdownlist containing the enum values' names according to their DisplayAttribute.
The actual retrieval of the displayed enum value is done in the GetName method, which first uses the GetDisplayAttribute method to try to retrieve the DisplayAttribute of the enum value. If the enum value was indeed decorated with a DisplayAttribute, it will use that to retrieve the name to be displayed. If there is no DisplayAttribute, we just return the name of the enum value.

As was to be expected, Microsoft included an EnumDropDownListFor HTML Helper method in MVC 5.1 (RC1).

Related

MVC html helper to generate enum drop down list dynamiclly

My model is:
public class DynamicEnum
{
public string Name {get; set;}
public int Value {get; set;}
}
public class ViewModel
{
public DynamicEnum DynamicEnum {get; set;}
}
Public ActionResult MyAction
{
var model = new ViewModel();
model.DynamicEnum = new DynamicEnum(){ Name = "System.DayOfWeek", Value = 2};
return View(model);
}
So in the view I need a HtmlHelper to dynamically generate DropDownListFor like:
#Html.EnumDropDownListFor(m => m.DynamicEnum)
I used MVC 5.2.3, Does any one have any idea?
One way to achieve this would be to use reflection:
public static class HtmlExtensions
{
public static IHtmlString EnumDropDownListFor<TModel>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, DynamicEnum>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var dynamicEnum = (DynamicEnum)metadata.Model;
var enumType = Type.GetType(dynamicEnum.Name, true);
if (!enumType.IsEnum)
{
throw new Exception(dynamicEnum.Name + " doesn't represent a valid enum type");
}
// TODO: You definetely want to cache the values here to avoid the expensive
// reflection call: a ConcurrentDictionary<Type, IList<SelectListItem>> could be used
var enumNames = Enum.GetNames(enumType);
var values = enumNames.Select(x => new SelectListItem
{
Text = x,
Value = ((int)Enum.Parse(enumType, x)).ToString(),
}).ToList();
string name = ExpressionHelper.GetExpressionText(expression) + ".Value";
return html.DropDownList(name, values);
}
}
Remark: The HtmlHelper.EnumDropDownListFor extension method already exists in ASP.NET MVC so make sure that you bring the namespace in which you declared your custom extension method into scope to avoid collisions. Or just use a different method name.

MVC: Access display name attribute in the controller

Is it possible to access the display name of a parameter in the controller? for example, say I defined a parameter as
public class Class1
{
[DisplayName("First Name")]
public string firstname { get; set; }
}
I now want to be able to access the display name of firstname in my controller. Something like
string name = Model.Class1.firstName.getDisplayName();
Is there a method like getDisplayName() that I can use to get the display name?
First off, you need to get a MemberInfo object that represents that property. You will need to do some form of reflection:
MemberInfo property = typeof(Class1).GetProperty("Name");
(I'm using "old-style" reflection, but you can also use an expression tree if you have access to the type at compile-time)
Then you can fetch the attribute and obtain the value of the DisplayName property:
var attribute = property.GetCustomAttributes(typeof(DisplayNameAttribute), true)
.Cast<DisplayNameAttribute>().Single();
string displayName = attribute.DisplayName;
Found the answer at this link. I created an Html helper class, added its namespace to my view web.config and used it in my controller. All described in the link
Display name for Enum is like this
Here is example
public enum Technology
{
[Display(Name = "AspNet Technology")]
AspNet,
[Display(Name = "Java Technology")]
Java,
[Display(Name = "PHP Technology")]
PHP,
}
and method like this
public static string GetDisplayName(this Enum value)
{
var type = value.GetType();
var members = type.GetMember(value.ToString());
if (members.Length == 0) throw new ArgumentException(String.Format("error '{0}' not found in type '{1}'", value, type.Name));
var member = members[0];
var attributes = member.GetCustomAttributes(typeof(DisplayAttribute), false);
if (attributes.Length == 0) throw new ArgumentException(String.Format("'{0}.{1}' doesn't have DisplayAttribute", type.Name, value));
var attribute = (DisplayAttribute)attributes[0];
return attribute.GetName();
}
And your controller like this
public ActionResult Index()
{
string DisplayName = Technology.AspNet.GetDisplayName();
return View();
}
for class property follow this step
public static string GetDisplayName2<TSource, TProperty> (Expression<Func<TSource, TProperty>> expression)
{
var attribute = Attribute.GetCustomAttribute(((MemberExpression)expression.Body).Member, typeof(DisplayAttribute)) as DisplayAttribute;
return attribute.GetName();
}
and call this method in your controller like this
// Class1 is your classname and firstname is your property of class
string localizedName = Testing.GetDisplayName2<Class1, string>(i => i.firstname);

MVC Razor RadioButtonList without any preselected values

Im using RadioButtonList to generate my radio buttons, in a MVC Razor project.
Here's an example of my code:
#Html.RadioButtonList(n => n.HouseType)
For some reason my radio button lists gets a preselected value. The first checkbox is always checked, which makes my UI kinda confusing.
How do I disable this in a good way?
One way is to loop through the whole page with Jquery and unselect each box. But thats not a pretty work around imho.
EDIT:
Here's more info about HouseType, which is a custom enum.
public enum HouseType
{
House,
Apartment,
Garage
};
and its called upon by using this line
public HouseType HouseType { get; set; }
You could make the HouseType property a nullable type on your view model. For example if it is an enum type:
public HouseTypes? HouseType { get; set; }
or if it is an integer:
public int? HouseType { get; set; }
UPDATE:
It seems that you are using the following helper. This helper doesn't support nullable enum values. So adapt it:
public static class RaidioButtonListHelper
{
/// <summary>
/// Create a radiobutton list from viewmodel.
/// </summary>
public static MvcHtmlString RadioButtonList<TModel, TResult>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TResult>> expression, IEnumerable<SelectListItem> listOfValues = null)
{
var typeOfProperty = expression.ReturnType;
// Added by Darin Dimitrov to support nullable enums
var underlyingType = Nullable.GetUnderlyingType(typeOfProperty);
if (underlyingType != null)
{
typeOfProperty = underlyingType;
}
// End of addition
if (listOfValues == null && typeOfProperty.IsEnum)
{
listOfValues = new SelectList(Enum.GetValues(typeOfProperty));
}
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
// Ctreat table
TagBuilder tableTag = new TagBuilder("table");
tableTag.AddCssClass("radio-main");
// Create tr:s
var trTagLable = new TagBuilder("tr id=\"" + metaData.PropertyName + "Lables\"");
var trTagRadio = new TagBuilder("tr id=\"" + metaData.PropertyName + "Radios\"");
foreach (SelectListItem item in listOfValues)
{
var text = item.Text;
var value = item.Value ?? text;
// Generate an id to be given to the radio button field
var id = string.Format("{0}_{1}", metaData.PropertyName, value);
// Create the radiobuttons
var radioTag = htmlHelper.RadioButtonFor(expression, value, new { id = id }).ToHtmlString();
// Create the label for the radiobuttons.
var labelTag = htmlHelper.Label(id, HttpUtility.HtmlEncode(text));
// Add the lables and reaiobuttons to td:s
var tdTagLable = new TagBuilder("td style=\"padding-left: 10px; text-align: center\"");
var tdTagRadio = new TagBuilder("td style=\"padding-left: 10px; text-align: center\"");
tdTagLable.InnerHtml = labelTag.ToString();
tdTagRadio.InnerHtml = radioTag.ToString();
// Add tds: to tr:s
trTagLable.InnerHtml += tdTagLable.ToString();
trTagRadio.InnerHtml += tdTagRadio.ToString();
}
// Add tr:s to table
tableTag.InnerHtml = trTagLable.ToString() + trTagRadio.ToString();
//Return the table tag
return new MvcHtmlString(tableTag.ToString());
}
}
Now it's gonna work with a nullable enum and it won't select any radio button if the value of the corresponding property is null.

Selected Property of SelectListItem and selectedValue parameter of SelectItem not working

I am trying to develop an HtmlHelper extension method: EnumDropDownListFor. No matter what I did I was unable to show the selected value. I tried setting Selected=true property of SelectListItem and setting selectedValue of SelectList constructor. While debugging (at return line) I can see Selected=true for the SelectLİstItem which is supposed to be Selected, for both cases. But when I "View Source" none of the options have selected="selected" attribute.
Where am I going wrong?
Note: Toolkit is my utility class and ToByte is an extension method for Enum
public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string optionLabel = null,
object htmlAttributes = null) where TModel : class
{
var selectedValue = helper.ViewData.Model == null
? default(TProperty)
: expression.Compile()(helper.ViewData.Model);
var enumVals = Toolkit.GetEnumValues(typeof(TProperty));
//var selectList = from enumVal in enumVals.OfType<Enum>()
// select new SelectListItem
// {
// Text = enumVal.GetName(),
// Value = enumVal.ToByte().ToString(),
// Selected = Equals(enumVal, Toolkit.To<Enum>(selectedValue))
// };
// helper.ViewData[(expression.Body as MemberExpression).Member.Name] = Toolkit.To<Enum>(selectedValue).ToByte().ToString();
var selectList = new SelectList(from enumVal in enumVals.OfType<Enum>()
select new
{
TextField = enumVal.GetName(),
ValueField = enumVal.ToByte().ToString()
}, "ValueField", "TextField", Toolkit.To<Enum>(selectedValue).ToByte().ToString());
return helper.DropDownListFor(expression, selectList, optionLabel, htmlAttributes);
}
I solved it (:
That was beacuse I am calling "helper.DropDownListFor" with same expression which returns an Enum type and I was trying to set values of options by "Byte" casted values. So it seems that Expression's return value overrides the given selected value, makes a lot of sense.

How to access Dataannotation's "GroupName" in mvc view?

I want to access DataAnnotation's GroupName in MVC view.
For example, let's say one of my model properties is like
[Display(Order = 1, GroupName= "Passport Detail", Name = "Passport #")]
[Required()]
[StringLength(20)]
public string PassportNo { get; set; }
Now, How can I access the GroupName in the view?
I want to achieve something like this in MVC.
I was looking into this as well and it seems like you have to do a lot of code for an attribute that should simply just be there...right??
Rather than having to extend the current logic, I went with the AdditionalValuesAttribute to define my own "GroupName". Of course you can get all "fancy pants" with extending this, but it basically works like so:
if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("GroupName")) {
var groupName = ViewData.ModelMetadata.AdditionalValues["GroupName"].ToString();
}
I found a workaround solution.
We can achive this by using DataAnnotationsModelMetadataProvider.
For Ex.
public class MetadataProvider : 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 disp = attributes.OfType<DisplayAttribute>().FirstOrDefault();
if (disp != null)
{
if (disp.GroupName != null)
metadata.AdditionalValues.Add("GroupName", disp.GroupName);
}
return metadata;
}
}
In the mvc view we can use AdditionalValues as usual.
Well, I dont see that MVC uses that. DataAnnotations is not really part of MVC so all props may not be used. If you wanted to use it I would say you need to create an HTML helper that could create those groups.
Ok, I think I see what you might want... you want it to auto template it for you. Just use the EditorTemplates. http://www.codecapers.com/post/Display-and-Editor-Templates-in-ASPNET-MVC-2.aspx
How About This!!! must work :
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
namespace System.Web.Mvc
{
public static class DisplayGroup
{
public static MvcHtmlString DisplayGroupName(this HtmlHelper helper, string groupName)
{
return MvcHtmlString.Create(groupName);
}
public static MvcHtmlString DisplayGroupNameFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
var type = typeof(TModel);
PropertyInfo propertyInfo = null;
var member = (MemberExpression)expression.Body;
var property = (PropertyInfo)member.Member;
var name = property.Name;
var metadataTypeInfo = type.GetCustomAttribute<MetadataTypeAttribute>();
if (metadataTypeInfo != null)
{
var metadataType = metadataTypeInfo.MetadataClassType;
propertyInfo = metadataType.GetProperties().Where(x => x.Name == name).FirstOrDefault();
if (propertyInfo == null)
{
propertyInfo = type.GetProperties().Where(x => x.Name == name).FirstOrDefault();
}
}
else
{
propertyInfo = type.GetProperties().Where(x => x.Name == name).FirstOrDefault();
}
string output = "";
var dattr = propertyInfo.GetCustomAttribute<DisplayAttribute>();
if (dattr != null)
{
if (dattr.GroupName == null)
{
output = propertyInfo.Name;
}
else
{
output = dattr.GroupName;
}
}
else
{
output = propertyInfo.Name;
}
return MvcHtmlString.Create(output);
}
}
}
public class MyModel
{
[Display(Name = "Number",GroupName="Invoice")]
string InvNo { get; set; }
}
and then simply write :
#Html.DisplayGroupNameFor(x => x.InvNo)
Note :
NameSpace should be : System.Web.Mvc
Update :
The cool thing is that , if you have a MetaDataType class defined for your dataAnnotation , then also this will work as expected.

Resources