Optgroup in DropDownlistFor MVC 4 Not Support Mvc validation - asp.net-mvc

public static MvcHtmlString DropDownGroupListFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, IEnumerable<GroupedSelectListItem> selectList, string defaultText, object htmlAttributes, bool isDisabled)
{
if (isDisabled)
{
IDictionary<string, object> htmlAttributeMerge = new RouteValueDictionary(htmlAttributes);
htmlAttributeMerge.Add("disabled", "disabled");
return DropDownGroupListFor(htmlHelper,expression, selectList, defaultText, htmlAttributeMerge);
}
else
{
return DropDownGroupListFor(htmlHelper, expression, selectList, defaultText, htmlAttributes);
}
}
// Helper methods
private static IEnumerable<GroupedSelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)
{
object o = null;
if (htmlHelper.ViewData != null)
{
o = htmlHelper.ViewData.Eval(name);
}
if (o == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
"Missing Select Data",
name,
"IEnumerable<GroupedSelectListItem>"));
}
IEnumerable<GroupedSelectListItem> selectList = o as IEnumerable<GroupedSelectListItem>;
if (selectList == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
"Wrong Select DataType",
name,
o.GetType().FullName,
"IEnumerable<GroupedSelectListItem>"));
}
return selectList;
}
internal static string ListItemToOption(GroupedSelectListItem item)
{
TagBuilder builder = new TagBuilder("option")
{
InnerHtml = HttpUtility.HtmlEncode(item.Text)
};
if (item.Value != null)
{
builder.Attributes["value"] = item.Value;
}
if (item.Selected)
{
builder.Attributes["selected"] = "selected";
}
return builder.ToString(TagRenderMode.Normal);
}
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, string optionLabel, string name, IEnumerable<GroupedSelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes)
{
name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (String.IsNullOrEmpty(name))
{
throw new ArgumentException("Null Or Empty", "name");
}
bool usedViewData = false;
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = GetSelectData(htmlHelper, name);
usedViewData = true;
}
object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(name, typeof(string[])) : htmlHelper.GetModelStateValue(name, typeof(string));
// If we haven't already used ViewData to get the entire list of items then we need to
// use the ViewData-supplied value before using the parameter-supplied value.
if (!usedViewData)
{
if (defaultValue == null)
{
defaultValue = htmlHelper.ViewData.Eval(name);
}
}
if (defaultValue != null)
{
IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture);
HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
List<GroupedSelectListItem> newSelectList = new List<GroupedSelectListItem>();
foreach (GroupedSelectListItem item in selectList)
{
item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
newSelectList.Add(item);
}
selectList = newSelectList;
}
// Convert each ListItem to an <option> tag
StringBuilder listItemBuilder = new StringBuilder();
// Make optionLabel the first item that gets rendered.
if (optionLabel != null)
{
listItemBuilder.AppendLine(ListItemToOption(new GroupedSelectListItem() { Text = optionLabel, Value = String.Empty, Selected = false }));
}
foreach (var group in selectList.GroupBy(i => i.GroupKey))
{
string groupName = selectList.Where(i => i.GroupKey == group.Key).Select(it => it.GroupName).FirstOrDefault();
listItemBuilder.AppendLine(string.Format("<optgroup label=\"{0}\" value=\"{1}\">", groupName, group.Key));
foreach (GroupedSelectListItem item in group)
{
listItemBuilder.AppendLine(ListItemToOption(item));
}
listItemBuilder.AppendLine("</optgroup>");
}
TagBuilder tagBuilder = new TagBuilder("select")
{
InnerHtml = listItemBuilder.ToString()
};
foreach (var htmlAttribute in htmlAttributes)
{
tagBuilder.MergeAttribute(
htmlAttribute.Key.Replace('_', '-'),
(string)htmlAttribute.Value
);
}
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("name", name, true /* replaceExisting */);
tagBuilder.GenerateId(name);
if (allowMultiple)
{
tagBuilder.MergeAttribute("multiple", "multiple");
}
// If there are any errors for a named field, we add the css attribute.
ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState))
{
if (modelState.Errors.Count > 0)
{
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
}
return MvcHtmlString.Create(tagBuilder.ToString());
}
internal static object GetModelStateValue(this HtmlHelper helper, string key, Type destinationType)
{
ModelState modelState;
if (helper.ViewData.ModelState.TryGetValue(key, out modelState))
{
if (modelState.Value != null)
{
return modelState.Value.ConvertTo(destinationType, null /* culture */);
}
}
return null;
}
#Html.DropDownGroupListFor(m => m.id, Model.list, null, new { #class
= "form-control"})
this the whole code Validation On Id But validtion not work,validation is server side when click on button to submit validtion not fire why?

Related

Custom DropDownListFor Helper loads an empty value for the selected element

I am using a custom helper, to create a select element that can take HtmlAttribute. I am using the same code as I found here, in #Alexander Puchkov answer (which I'll post for reference).
It's working fine, apart when the DDL helper is loaded with one of the option as the selected item, then the value of the selected item is null/empty. (i.e. on an edit page the DDL loads up with the option that was set on creation, rather than the '-Please select option-'),
The highlighted attribute in the picture shows the problem, the value should not be empty, but should show 'Medium'...
So the text is display correctly but the element has no value. Any ideas on where this issue comes from?
Here is the full code of the helper:
/// <summary>
/// A selectListItem with an extra property to hold HtmlAttributes
/// <para>Used in conjunction with the sdDDL Helpers</para>
/// </summary>
public class SdSelectListItem : SelectListItem
{
public object HtmlAttributes { get; set; }
}
/// <summary>
/// Generate DropDownLists with the possibility of styling the 'option' tags of the generated 'select' tag
/// </summary>
public static class SdDdlHelper
{
public static MvcHtmlString sdDDLFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression, IEnumerable<SdSelectListItem> selectList,
string optionLabel, object htmlAttributes)
{
if (expression == null)
throw new ArgumentNullException("expression");
ModelMetadata metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData);
return SelectInternal(htmlHelper, metadata, optionLabel, ExpressionHelper.GetExpressionText(expression), selectList,
false /* allowMultiple */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString sdDDLFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression, IEnumerable<SdSelectListItem> selectList,
object htmlAttributes)
{
if (expression == null)
throw new ArgumentNullException("expression");
ModelMetadata metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData);
//-> The below line is my problem (specifically the 'null' param), it set to null if no option label is passed to the method...So if I use this overload, the DDL will load (or re-load) with the default value selected, not the value binded to the property - And if I use the above overload, and set Model.action_priority as the optionLabel, then I get what is shown in the picture...
return SelectInternal(htmlHelper, metadata, null, ExpressionHelper.GetExpressionText(expression), selectList,
false /* allowMultiple */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
#region internal/private methods
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name,
IEnumerable<SdSelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
{
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (String.IsNullOrEmpty(fullName))
throw new ArgumentException("No name");
if (selectList == null)
throw new ArgumentException("No selectlist");
object defaultValue = (allowMultiple)
? htmlHelper.GetModelStateValue(fullName, typeof(string[]))
: htmlHelper.GetModelStateValue(fullName, typeof(string));
// If we haven't already used ViewData to get the entire list of items then we need to
// use the ViewData-supplied value before using the parameter-supplied value.
if (defaultValue == null)
defaultValue = htmlHelper.ViewData.Eval(fullName);
if (defaultValue != null)
{
IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
IEnumerable<string> values = from object value in defaultValues
select Convert.ToString(value, CultureInfo.CurrentCulture);
HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
List<SdSelectListItem> newSelectList = new List<SdSelectListItem>();
foreach (SdSelectListItem item in selectList)
{
item.Selected = (item.Value != null)
? selectedValues.Contains(item.Value)
: selectedValues.Contains(item.Text);
newSelectList.Add(item);
}
selectList = newSelectList;
}
// Convert each ListItem to an <option> tag
StringBuilder listItemBuilder = new StringBuilder();
// Make optionLabel the first item that gets rendered.
if (optionLabel != null)
listItemBuilder.Append(
ListItemToOption(new SdSelectListItem()
{
Text = optionLabel,
Value = String.Empty,
Selected = false
}));
foreach (SdSelectListItem item in selectList)
{
listItemBuilder.Append(ListItemToOption(item));
}
TagBuilder tagBuilder = new TagBuilder("select")
{
InnerHtml = listItemBuilder.ToString()
};
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("name", fullName, true /* replaceExisting */);
tagBuilder.GenerateId(fullName);
if (allowMultiple)
tagBuilder.MergeAttribute("multiple", "multiple");
// If there are any errors for a named field, we add the css attribute.
System.Web.Mvc.ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
{
if (modelState.Errors.Count > 0)
{
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
}
tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(fullName, metadata));
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
}
internal static string ListItemToOption(SdSelectListItem item)
{
TagBuilder builder = new TagBuilder("option")
{
InnerHtml = HttpUtility.HtmlEncode(item.Text)
};
if (item.Value != null)
{
builder.Attributes["value"] = item.Value;
}
if (item.Selected)
{
builder.Attributes["selected"] = "selected";
}
builder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(item.HtmlAttributes));
return builder.ToString(TagRenderMode.Normal);
}
internal static object GetModelStateValue(this HtmlHelper htmlHelper, string key, Type destinationType)
{
System.Web.Mvc.ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(key, out modelState))
{
if (modelState.Value != null)
{
return modelState.Value.ConvertTo(destinationType, null /* culture */);
}
}
return null;
}
#endregion
}
}
Here is how I call it in the View (using Razor):
#Html.sdDDLFor(x => Model.action_priority, Model.actionPriorityDDL(), Model.action_priority, new
{
#id = "_Action_Priority_DDL",
#class = "form-control"
})
And finally here is the Model.actionPriorityDDL() method:
public List<SdSelectListItem> actionPriorityDDL()
{
action_priority_DDL = new List<SdSelectListItem>();
action_priority_DDL.Add(new SdSelectListItem
{
Value = StringRepository.ActionPriority.high,
Text = StringRepository.ActionPriority.high,
HtmlAttributes = new
{
#class = "lbl-action-priority-high"
}
}
);
action_priority_DDL.Add(new SdSelectListItem
{
Value = StringRepository.ActionPriority.medium,
Text = StringRepository.ActionPriority.medium,
HtmlAttributes = new
{
#class = "lbl-action-priority-medium"
}
}
);
action_priority_DDL.Add(new SdSelectListItem
{
Value = StringRepository.ActionPriority.low,
Text = StringRepository.ActionPriority.low,
HtmlAttributes = new
{
#class = "lbl-action-priority-low"
}
}
);
action_priority_DDL.Add(new SdSelectListItem
{
Value = StringRepository.ActionPriority.psar,
Text = StringRepository.ActionPriority.psar,
HtmlAttributes = new
{
#class = "lbl-action-priority-psar"
}
}
);
return action_priority_DDL;
}
This is caused by a former bug in MVC framework (http://aspnet.codeplex.com/workitem/8311; accessible here: https://web.archive.org/web/20131208041521/http://aspnet.codeplex.com/workitem/8311) that happens when using the DropDownListFor helper within a loop, i.e. the model property has an indexer (like in your example, where the generated select element has an attribute name="actionList[0].action_priority")
I resolved this by copying some code from here https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/Html/SelectExtensions.cs
Specifically, in this method
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name,
IEnumerable<ExtendedSelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
replace this
if (selectList == null)
throw new ArgumentException("No selectlist");
with:
bool usedViewData = false;
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = htmlHelper.GetSelectData(name);
usedViewData = true;
}
Now, replace this
if (defaultValue == null)
defaultValue = htmlHelper.ViewData.Eval(fullName);
with:
if (defaultValue == null && !String.IsNullOrEmpty(name))
{
if (!usedViewData)
{
defaultValue = htmlHelper.ViewData.Eval(name);
}
else if (metadata != null)
{
defaultValue = metadata.Model;
}
}
Finally, you also need to add this method:
private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)
{
object o = null;
if (htmlHelper.ViewData != null)
{
o = htmlHelper.ViewData.Eval(name);
}
if (o == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.HtmlHelper_MissingSelectData,
name,
"IEnumerable<SelectListItem>"));
}
IEnumerable<SelectListItem> selectList = o as IEnumerable<SelectListItem>;
if (selectList == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.HtmlHelper_WrongSelectDataType,
name,
o.GetType().FullName,
"IEnumerable<SelectListItem>"));
}
return selectList;
}
replacing SelectListItem with your custom class (e.g. SdSelectListItem, or ExtendedSelectListItem , or whatever you named it )

How to Use check box list in MVC

I need to display check box list with more than one options. User must select minimum one check box, user can select more than one check boxes.
I want to store values of all check boxes (selected) in one field as a string(Data base) with coma separated. This is not mandatory, mandatory is need to store multiple values of each selected check box. Alternate solutions are welcome.
Model
public class Member
{
public string Member_VehicalType { get; set; }
public IList<SelectListItem> Member_VehicalType_List { get; set; }
Controller
[HttpGet]
public ActionResult Create()
{
Member objMemberModel = new Member();
List<SelectListItem> vehical_Types = new List<SelectListItem>();
vehical_Types.Add(new SelectListItem { Text = "Two Wheeler", Value = "1" });
vehical_Types.Add(new SelectListItem { Text = "Four Wheeler", Value = "2" });
objMemberModel.Member_VehicalType_List = vehical_Types;
return View(objMemberModel);
How do I create view with #Html.CheckBoxFor( or #Html.CheckBox(
I recently had to deal with SelectListItem as a checkbox-list. I came up with the following HtmlExtensions, which might be helpful. These extensions provide the same functionality as the #Html.DropDownListFor(model => ...);
Usage: #Html.CheckBoxListFor(model => model.ModelMemberToPutValueIn, Model.Member_VehicalType_List)
public static class HtmlExtensions
{
public static MvcHtmlString CheckBoxListFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, IEnumerable<SelectListItem> items, object htmlAttributes = null)
{
var listName = ExpressionHelper.GetExpressionText(expression);
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
items = GetCheckboxListWithDefaultValues(metaData.Model, items);
return htmlHelper.CheckBoxList(listName, items, htmlAttributes);
}
public static MvcHtmlString CheckBoxList(this HtmlHelper htmlHelper, string listName, IEnumerable<SelectListItem> items, object htmlAttributes = null)
{
if (items == null) return null;
var container = new TagBuilder("div");
container.AddCssClass("checkbox-list");
container.MergeAttribute("id", HtmlHelper.GenerateIdFromName(listName));
foreach (var item in items)
{
var id = HtmlHelper.GenerateIdFromName(String.Join(".", listName, item.Text));
var div = new TagBuilder("div");
div.AddCssClass("checkbox");
var cb = new TagBuilder("input");
cb.MergeAttribute("type", "checkbox");
cb.MergeAttribute("id", id);
cb.MergeAttribute("name", listName);
cb.MergeAttribute("value", item.Value ?? item.Text);
if (item.Selected) cb.MergeAttribute("checked", "checked");
var label = new TagBuilder("label");
label.MergeAttribute("for", id);
label.MergeAttributes(new RouteValueDictionary(htmlAttributes), true);
label.InnerHtml = item.Text;
div.InnerHtml = cb.ToString(TagRenderMode.SelfClosing) + label;
container.InnerHtml += div;
}
return new MvcHtmlString(container.ToString());
}
private static IEnumerable<SelectListItem> GetCheckboxListWithDefaultValues(object defaultValues, IEnumerable<SelectListItem> selectList)
{
var defaultValuesList = defaultValues as IEnumerable;
if (defaultValuesList == null) return selectList;
var values = from object value in defaultValuesList select Convert.ToString(value, CultureInfo.CurrentCulture);
var selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
var newSelectList = new List<SelectListItem>();
foreach (var item in selectList)
{
item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
newSelectList.Add(item);
}
return newSelectList;
}
}

Get [DisplayName] attribute of a property in strongly-typed way

Good day!
I've such method to get [DisplayName] attribute value of a property (which is attached directly or using [MetadataType] attribute). I use it in rare cases where I need to get [DisplayName] in controller code.
public static class MetaDataHelper
{
public static string GetDisplayName(Type dataType, string fieldName)
{
// First look into attributes on a type and it's parents
DisplayNameAttribute attr;
attr = (DisplayNameAttribute)dataType.GetProperty(fieldName).GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
// Look for [MetadataType] attribute in type hierarchy
// http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
if (attr == null)
{
MetadataTypeAttribute metadataType = (MetadataTypeAttribute)dataType.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
if (metadataType != null)
{
var property = metadataType.MetadataClassType.GetProperty(fieldName);
if (property != null)
{
attr = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
}
}
}
return (attr != null) ? attr.DisplayName : String.Empty;
}
}
It works, but it has two drawbacks:
It requires field name as string
It doesn't work if I want to get property of a property
Is it possible to overcome both problems using lambdas, something like we have in ASP.NET MVC:
Html.LabelFor(m => m.Property.Can.Be.Very.Complex.But.Strongly.Typed);
Update
Here is an updated and checked version from BuildStarted solution. It is modified to use DisplayName attribute (you can modify back to Display attribute if you use it). And fixed minor bugs to get attribute of nested properties.
public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression)
{
Type type = typeof(TModel);
string propertyName = null;
string[] properties = null;
IEnumerable<string> propertyList;
//unless it's a root property the expression NodeType will always be Convert
switch (expression.Body.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
var ue = expression.Body as UnaryExpression;
propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
break;
default:
propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
break;
}
//the propert name is what we're after
propertyName = propertyList.Last();
//list of properties - the last property name
properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties
foreach (string property in properties)
{
PropertyInfo propertyInfo = type.GetProperty(property);
type = propertyInfo.PropertyType;
}
DisplayNameAttribute attr;
attr = (DisplayNameAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
// Look for [MetadataType] attribute in type hierarchy
// http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
if (attr == null)
{
MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
if (metadataType != null)
{
var property = metadataType.MetadataClassType.GetProperty(propertyName);
if (property != null)
{
attr = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
}
}
}
return (attr != null) ? attr.DisplayName : String.Empty;
}
There are two ways to do this:
Models.Test test = new Models.Test();
string DisplayName = test.GetDisplayName(t => t.Name);
string DisplayName = Helpers.GetDisplayName<Models.Test>(t => t.Name);
The first one works by virtue of writing a generic extension method to any TModel (which is all types). This means it will be available on any object and not just your model. Not really recommended but nice because of it's concise syntax.
The second method requires you to pass in the Type of the model it is - which you're already doing but as a parameter instead. This method is required to define type via Generics because Func expects it.
Here are the methods for you to check out.
Static extension method to all objects
public static string GetDisplayName<TModel, TProperty>(this TModel model, Expression<Func<TModel, TProperty>> expression) {
Type type = typeof(TModel);
MemberExpression memberExpression = (MemberExpression)expression.Body;
string propertyName = ((memberExpression.Member is PropertyInfo) ? memberExpression.Member.Name : null);
// First look into attributes on a type and it's parents
DisplayAttribute attr;
attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();
// Look for [MetadataType] attribute in type hierarchy
// http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
if (attr == null) {
MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
if (metadataType != null) {
var property = metadataType.MetadataClassType.GetProperty(propertyName);
if (property != null) {
attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
}
}
}
return (attr != null) ? attr.Name : String.Empty;
}
Signature for type specific method - same code as above just different call
public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) { }
The reason you can't just use Something.GetDisplayName(t => t.Name) on it's own is because in the Razor engine you're actually passing an instantiated object of HtmlHelper<TModel> which is why the first method requires an instantiated object - This is only required for the compiler to infer what types belong to which generic name.
Update with recursive properties
public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) {
Type type = typeof(TModel);
string propertyName = null;
string[] properties = null;
IEnumerable<string> propertyList;
//unless it's a root property the expression NodeType will always be Convert
switch (expression.Body.NodeType) {
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
var ue = expression.Body as UnaryExpression;
propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
break;
default:
propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
break;
}
//the propert name is what we're after
propertyName = propertyList.Last();
//list of properties - the last property name
properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties
Expression expr = null;
foreach (string property in properties) {
PropertyInfo propertyInfo = type.GetProperty(property);
expr = Expression.Property(expr, type.GetProperty(property));
type = propertyInfo.PropertyType;
}
DisplayAttribute attr;
attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();
// Look for [MetadataType] attribute in type hierarchy
// http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
if (attr == null) {
MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
if (metadataType != null) {
var property = metadataType.MetadataClassType.GetProperty(propertyName);
if (property != null) {
attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
}
}
}
return (attr != null) ? attr.Name : String.Empty;
}
Late to the game, but...
I created a helper method using ModelMetadata like #Daniel mentioned and I thought I'd share it:
public static string GetDisplayName<TModel, TProperty>(
this TModel model
, Expression<Func<TModel, TProperty>> expression)
{
return ModelMetadata.FromLambdaExpression<TModel, TProperty>(
expression,
new ViewDataDictionary<TModel>(model)
).DisplayName;
}
Example Usage:
Models:
public class MySubObject
{
[DisplayName("Sub-Awesome!")]
public string Sub { get; set; }
}
public class MyObject
{
[DisplayName("Awesome!")]
public MySubObject Prop { get; set; }
}
Use:
HelperNamespace.GetDisplayName(Model, m => m.Prop) // "Awesome!"
HelperNamespace.GetDisplayName(Model, m => m.Prop.Sub) // "Sub-Awesome!"
Just do this:
using System.ComponentModel;
using System.Linq;
using System.Reflection;
namespace yournamespace
{
public static class ExtensionMethods
{
public static string GetDisplayName(this PropertyInfo prop)
{
if (prop.CustomAttributes == null || prop.CustomAttributes.Count() == 0)
return prop.Name;
var displayNameAttribute = prop.CustomAttributes.Where(x => x.AttributeType == typeof(DisplayNameAttribute)).FirstOrDefault();
if (displayNameAttribute == null || displayNameAttribute.ConstructorArguments == null || displayNameAttribute.ConstructorArguments.Count == 0)
return prop.Name;
return displayNameAttribute.ConstructorArguments[0].Value.ToString() ?? prop.Name;
}
}
}
Example as requested:
var props = typeof(YourType).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead);
var propFriendlyNames = props.Select(x => x.GetDisplayName());
I totally agree with the solution BuildStarted provided. The only thing i would change is that the ExtensionsMethode does not support translations. To support this is simple minor change is needed. I would have put this in the comments but i don't have enough points to do so. Look for the last line in the method.
The Extension Method
public static string GetDisplayName<TModel, TProperty>(this TModel model, Expression<Func<TModel, TProperty>> expression)
{
Type type = typeof(TModel);
IEnumerable<string> propertyList;
//unless it's a root property the expression NodeType will always be Convert
switch (expression.Body.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
var ue = expression.Body as UnaryExpression;
propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
break;
default:
propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
break;
}
//the propert name is what we're after
string propertyName = propertyList.Last();
//list of properties - the last property name
string[] properties = propertyList.Take(propertyList.Count() - 1).ToArray();
Expression expr = null;
foreach (string property in properties)
{
PropertyInfo propertyInfo = type.GetProperty(property);
expr = Expression.Property(expr, type.GetProperty(property));
type = propertyInfo.PropertyType;
}
DisplayAttribute attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();
// Look for [MetadataType] attribute in type hierarchy
// http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
if (attr == null)
{
MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
if (metadataType != null)
{
var property = metadataType.MetadataClassType.GetProperty(propertyName);
if (property != null)
{
attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
}
}
}
//To support translations call attr.GetName() instead of attr.Name
return (attr != null) ? attr.GetName() : String.Empty;
}
I found another nice code snippet here, and I have slightly modified it for 'DisplayName' purpose
public static string GetDisplayName<TSource, TProperty>(Expression<Func<TSource, TProperty>> expression)
{
var attribute = Attribute.GetCustomAttribute(((MemberExpression)expression.Body).Member, typeof(DisplayNameAttribute)) as DisplayNameAttribute;
if (attribute == null)
{
throw new ArgumentException($"Expression '{expression}' doesn't have DisplayAttribute");
}
return attribute.DisplayName;
}
And usages
GetDisplayName<ModelName, string>(i => i.PropertyName)
I make a Little change is you are using resources to get the DisplayName
public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression)
{
string _ReturnValue = string.Empty;
Type type = typeof(TModel);
string propertyName = null;
string[] properties = null;
IEnumerable<string> propertyList;
//unless it's a root property the expression NodeType will always be Convert
switch (expression.Body.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
var ue = expression.Body as UnaryExpression;
propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
break;
default:
propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
break;
}
//the propert name is what we're after
propertyName = propertyList.Last();
//list of properties - the last property name
properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties
Expression expr = null;
foreach (string property in properties)
{
PropertyInfo propertyInfo = type.GetProperty(property);
expr = Expression.Property(expr, type.GetProperty(property));
type = propertyInfo.PropertyType;
}
DisplayAttribute attr;
attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();
// Look for [MetadataType] attribute in type hierarchy
// http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
if (attr == null)
{
MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
if (metadataType != null)
{
var property = metadataType.MetadataClassType.GetProperty(propertyName);
if (property != null)
{
attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
}
}
}
if (attr != null && attr.ResourceType != null)
_ReturnValue = attr.ResourceType.GetProperty(attr.Name).GetValue(attr).ToString();
else if (attr != null)
_ReturnValue = attr.Name;
return _ReturnValue;
}
Happy Coding
Another code snippet with code .Net uses itself to perform this
public static class WebModelExtensions
{
public static string GetDisplayName<TModel, TProperty>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TProperty>> expression)
{
// Taken from LabelExtensions
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string displayName = metadata.DisplayName;
if (displayName == null)
{
string propertyName = metadata.PropertyName;
if (propertyName == null)
{
var htmlFieldName = ExpressionHelper.GetExpressionText(expression);
displayName = ((IEnumerable<string>) htmlFieldName.Split('.')).Last<string>();
}
else
displayName = propertyName;
}
return displayName;
}
}
// Usage
Html.GetDisplayName(model => model.Password)
#Buildstarted Answer works but i wanted to get the DisplayName by property name rather using linq expression so i did a little change that will save your time
public static string GetDisplayNameByMemberName<TModel>(string memberName)
{
Type type = typeof(TModel);
string propertyName = memberName;
DisplayAttribute attr;
attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();
//Look for [MetadataType] attribute in type hierarchy
//http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
if (attr == null)
{
MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
if (metadataType != null)
{
var property = metadataType.MetadataClassType.GetProperty(propertyName);
if (property != null)
{
attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
}
}
}
return (attr != null) ? attr.Name : String.Empty;
}
I also wanted to get the localized value in the resources (.resx files) so :
string displayName = GeneralHelper.GetDisplayNameByMemberName<ViewModels.ProductVM>(memberName);
string displayNameTranslated = resourceManager.GetString(displayName, MultiLangHelper.CurrentCultureInfo);
This is a helper function i made to return an object of type "CultureInfo", create a culture info of some language or pass the current one
//MultiLangHelper.CurrentCultureInfo
return System.Threading.Thread.CurrentThread.CurrentCulture;

solution to the asp.net mvc drop down list selected value overrider problem

I wrote a helper method to display enums from my model in my asp.net MVC application as drop down lists in my views.
Here is my code for that:
public static List<SelectListItem> CreateSelectItemList<TEnum>(object enumObj,
string defaultItemKey,
bool sortAlphabetically,
object firstValueOverride)
where TEnum : struct
{
var values = (from v in (TEnum[])Enum.GetValues(typeof(TEnum))
select new
{
Id = Convert.ToInt32(v),
Name = ResourceHelpers.GetResourceValue(AppConstants.EnumResourceNamespace,
typeof(TEnum).Name, v.ToString())
});
return values.ToSelectList(e => e.Name,
e => e.Id.ToString(),
!string.IsNullOrEmpty(defaultItemKey) ? ResourceHelpers.GetResourceValue(AppConstants.EnumResourceNamespace, defaultItemKey) : string.Empty,
enumObj,
sortAlphabetically,
firstValueOverride);
}
This actually generates the select item list:
public static List<SelectListItem> ToSelectList<T>(
this IEnumerable<T> enumerable,
Func<T, string> text,
Func<T, string> value,
string defaultOption,
object selectedVal,
bool sortAlphabetically,
object FirstValueOverride)
{
int iSelectedVal = -1;
if(selectedVal!=null)
{
try
{
iSelectedVal = Convert.ToInt32(selectedVal);
}
catch
{
}
}
var items = enumerable.Select(f => new SelectListItem()
{
Text = text(f),
Value = value(f),
Selected = (iSelectedVal > -1)? (iSelectedVal.ToString().Equals(value(f))) : false
});
#region Sortare alfabetica
if (sortAlphabetically)
items = items.OrderBy(t => t.Text);
#endregion Sortare alfabetica
var itemsList = items.ToList();
Func<SelectListItem, bool> funct = null;
string sValue = string.Empty;
SelectListItem firstItem = null;
SelectListItem overridenItem = null;
int overridenIndex = 0;
if (FirstValueOverride != null)
{
sValue = FirstValueOverride.ToString();
funct = (t => t.Value == sValue);
overridenItem = itemsList.SingleOrDefault(funct);
overridenIndex = itemsList.IndexOf(overridenItem);
if (overridenItem != null)
{
firstItem = itemsList.ElementAt(0);
itemsList[0] = overridenItem;
itemsList[overridenIndex] = firstItem;
}
}
if(!string.IsNullOrEmpty(defaultOption))
itemsList.Insert(0, new SelectListItem()
{
Text = defaultOption,
Value = "-1"
});
return itemsList;
}
These is the method I call:
public static MvcHtmlString EnumDropDownList<TEnum>(this HtmlHelper htmlHelper,
object enumObj,
string name,
string defaultItemKey,
bool sortAlphabetically,
object firstValueOverride,
object htmlAttributes)
where TEnum : struct
{
return htmlHelper.DropDownList(name,
CreateSelectItemList<TEnum>(enumObj,
defaultItemKey,
sortAlphabetically,
firstValueOverride),
htmlAttributes);
}
Now I am having the problem described here
When I call this helper method and the input's name is the same as the property's name the selected value doesn't get selected.
The alternate solution described there doesn't work for me. The only solution that works is changing the name and not using the model binding using FormCollection instead.
I don't like this workaround because I can't use validation any more using the ViewModel pattern and I have to write some extra code for every enum.
I tried writing a custom model binder to compensate for this somehow but none of the methods I can override there gets called when I start the action.
Is there any way to do this without using FormCollection?
Can I somehow intercept ASP.NET MVC when it tries to put the value into my input field and make it select the right value?
Thank you in advance.
I have a sollution now
Here is the code for the EnumDropDownList function the other functions are listed in the question:
public static MvcHtmlString EnumDropDownList(this HtmlHelper htmlHelper,
object enumObj,
string name,
string defaultItemKey,
bool sortAlphabetically,
object firstValueOverride,
object htmlAttributes)
{
StringBuilder sbRezultat = new StringBuilder();
int selectedIndex = 0;
var selectItemList = new List<SelectListItem>();
if (enumObj != null)
{
selectItemList = CreateSelectItemList(enumObj, defaultItemKey, true, null);
var selectedItem = selectItemList.SingleOrDefault(item => item.Selected);
if (selectedItem != null)
{
selectedIndex = selectItemList.IndexOf(selectedItem);
}
}
var dict = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
TagBuilder tagBuilder = new TagBuilder("select");
tagBuilder.MergeAttribute("name", name,true);
bool bReadOnly = false;
//special case for readonly
if(dict.ContainsKey("readonly"))
{
//remove this tag it won't work the way mvc renders it anyway
dict.Remove("readonly");
bReadOnly = true;
}
//in case the style element is completed if the drop down is not readonly
tagBuilder.MergeAttributes(dict, true);
if (bReadOnly)
{
//add a small javascript to make it readonly and add the lightgrey style
tagBuilder.MergeAttribute("onchange", "this.selectedIndex=" + selectedIndex + ";",true);
tagBuilder.MergeAttribute("style", "background: lightgrey", true);
}
sbRezultat.Append(tagBuilder.ToString(TagRenderMode.StartTag));
foreach (var option in selectItemList)
{
sbRezultat.Append(" <option value='");
sbRezultat.Append(option.Value);
sbRezultat.Append("' ");
if (option.Selected)
sbRezultat.Append("selected");
sbRezultat.Append(" >");
sbRezultat.Append(option.Text);
sbRezultat.Append("</option>");
}
sbRezultat.Append("</select>");
return new MvcHtmlString(sbRezultat.ToString());
}
I also wrote a generic function of type For (EnumDropDownFor):
public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
string defaultItemKey,
bool sortAlphabetically,
object firstValueOverride,
object htmlAttributes)
where TProperty : struct
{
string inputName = GetInputName(expression);
object selectedVal = null;
try
{
selectedVal = htmlHelper.ViewData.Model == null
? default(TProperty)
: expression.Compile()(htmlHelper.ViewData.Model);
}
catch//in caz ca e ceva null sau ceva de genu'
{
}
return EnumDropDownList(htmlHelper,
selectedVal,
inputName,
defaultItemKey,
sortAlphabetically,
firstValueOverride,
htmlAttributes);
}
and some helper methods:
public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
{
if (expression.Body.NodeType == ExpressionType.Call)
{
MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
string name = GetInputName(methodCallExpression);
return name.Substring(expression.Parameters[0].Name.Length + 1);
}
return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
}
private static string GetInputName(MethodCallExpression expression)
{
MethodCallExpression methodCallExpression = expression.Object as MethodCallExpression;
if (methodCallExpression != null)
{
return GetInputName(methodCallExpression);
}
return expression.Object.ToString();
}

Radio Button List not being generated from identical code

I am currently pulling my hair out. I have an ASP.NET MVC web site with two forms, both have radio buttons on them. On the first page (that works), the radio list appears just fine. However, on the second page the radio buttons are not even present in the source code. Here is the code chunk from the first page (BestTimeType is an Enum that I made):
//Back End
[DisplayName("Time:")]
public RadioButtonListViewModel<BestTimeType> BestTimeRadioList { get; set; }
public EvalModel()
{
BestTimeRadioList = new RadioButtonListViewModel<BestTimeType>
{
Id = "BestTime",
SelectedValue = BestTimeType.Afternoon,
ListItems = new List<RadioButtonListItem<BestTimeType>>
{
new RadioButtonListItem<BestTimeType>{Text = "Morning", Value = BestTimeType.Morning},
new RadioButtonListItem<BestTimeType>{Text = "Afternoon", Value = BestTimeType.Afternoon},
new RadioButtonListItem<BestTimeType>{Text = "Evening", Value = BestTimeType.Evening}
}
};
}
// Front End
<div class="grid_1 evalLabel reqField" style="padding-top: 5px;">
<%= Html.LabelFor(model => model.BestTimeRadioList)%>
</div>
<div class="grid_4" style="text-align: center; padding: 5px 0px 10px 0px;">
<%= Html.RadioButtonListFor(model => model.BestTimeRadioList) %>
</div>
Here is the code chunk for the second page:
//Back End
[Required(ErrorMessage = "*")]
[DisplayName("HS Diploma:")]
public RadioButtonListViewModel<bool> HsDiplomaRadioList { get; set; }
public EmploymentModel()
{
HsDiplomaRadioList = new RadioButtonListViewModel<bool>
{
Id = "HsDiploma",
SelectedValue = false,
ListItems = new List<RadioButtonListItem<bool>>
{
new RadioButtonListItem<bool> {Text = "Yes", Value = true},
new RadioButtonListItem<bool> {Text = "No", Value = false}
}
};
}
//Front End
<div class="grid_2 employLabel reqField">
<%= Html.LabelFor(model => model.HsDiplomaRadioList) %>
</div>
<div class="grid_3">
<%= Html.RadioButtonListFor(model => model.HsDiplomaRadioList)%>
<%= Html.ValidationMessageFor(model => model.HsDiplomaRadioList)%>
</div>
I also tried making a custom Enum for the HsDiplomaRadioList (Yes/No), but the radio did not show up either.
I must be missing something extremely stupid simple. If any more code is necessary, I will be glad to put them up.
Thanks in advance.
Edit
Here is the code for RadioButtonList:
public static class HtmlHelperExtensions
{
public static string RadioButtonListFor<TModel, TRadioButtonListValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel<TRadioButtonListValue>>> expression) where TModel : class
{
return htmlHelper.RadioButtonListFor(expression, null);
}
public static string RadioButtonListFor<TModel, TRadioButtonListValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel<TRadioButtonListValue>>> expression, object htmlAttributes) where TModel : class
{
return htmlHelper.RadioButtonListFor(expression, new RouteValueDictionary(htmlAttributes));
}
public static string RadioButtonListFor<TModel, TRadioButtonListValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, RadioButtonListViewModel<TRadioButtonListValue>>> expression, IDictionary<string, object> htmlAttributes) where TModel : class
{
var inputName = GetInputName(expression);
RadioButtonListViewModel<TRadioButtonListValue> radioButtonList = GetValue(htmlHelper, expression);
if (radioButtonList == null)
return String.Empty;
if (radioButtonList.ListItems == null)
return String.Empty;
var divTag = new TagBuilder("div");
divTag.MergeAttribute("id", inputName);
divTag.MergeAttribute("class", "radio");
foreach (var item in radioButtonList.ListItems)
{
var radioButtonTag = RadioButton(htmlHelper, inputName, new SelectListItem { Text = item.Text, Selected = item.Selected, Value = item.Value.ToString() }, htmlAttributes);
divTag.InnerHtml += radioButtonTag;
}
return string.Concat(divTag, htmlHelper.ValidationMessage(inputName, "*"));
}
public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
{
if (expression.Body.NodeType == ExpressionType.Call)
{
var methodCallExpression = (MethodCallExpression)expression.Body;
string name = GetInputName(methodCallExpression);
return name.Substring(expression.Parameters[0].Name.Length + 1);
}
return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
}
private static string GetInputName(MethodCallExpression expression)
{
// p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw...
var methodCallExpression = expression.Object as MethodCallExpression;
if (methodCallExpression != null)
{
return GetInputName(methodCallExpression);
}
return expression.Object.ToString();
}
public static string RadioButton(this HtmlHelper htmlHelper, string name, SelectListItem listItem,
IDictionary<string, object> htmlAttributes)
{
var inputIdSb = new StringBuilder();
inputIdSb.Append(name)
.Append("_")
.Append(listItem.Value);
var sb = new StringBuilder();
var builder = new TagBuilder("input");
if (listItem.Selected) builder.MergeAttribute("checked", "checked");
builder.MergeAttribute("type", "radio");
builder.MergeAttribute("value", listItem.Value);
builder.MergeAttribute("id", inputIdSb.ToString());
builder.MergeAttribute("name", name + ".SelectedValue");
builder.MergeAttributes(htmlAttributes);
sb.Append(builder.ToString(TagRenderMode.SelfClosing));
sb.Append(RadioButtonLabel(inputIdSb.ToString(), listItem.Text, htmlAttributes));
//sb.Append("<br>");
return sb.ToString();
}
public static string RadioButtonLabel(string inputId, string displayText,
IDictionary<string, object> htmlAttributes)
{
var labelBuilder = new TagBuilder("label");
labelBuilder.MergeAttribute("for", inputId);
labelBuilder.MergeAttributes(htmlAttributes);
labelBuilder.InnerHtml = displayText;
return labelBuilder.ToString(TagRenderMode.Normal);
}
public static TProperty GetValue<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) where TModel : class
{
TModel model = htmlHelper.ViewData.Model;
if (model == null)
{
return default(TProperty);
}
Func<TModel, TProperty> func = expression.Compile();
return func(model);
}
}
Ok finally got around to stack tracing (I should do this first, but I was in a rush last night) and found that for some reason model.HsDiplomaRadio is null. I will need to track down the cause of this.

Resources