MVC 5 - How do I get an enums Display Name in my controller so that I can use it in emails? [duplicate] - asp.net-mvc

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;

Related

Syncfusion Server-Side event is not passing data

I have a Asp.Net MVC project created from Syncfusion ASP.New MVC (Essential JS 2) VS template that is using Syncfusion's Data Grid. I can get the CrudUpdate event set in CrudUrl to fire at the server, however the value returned to CrudUpdate is empty. action parameter seems correctly set.
If I cast the value as Object, I get back a not-null, but VS cannot interrogate it. My guess is some weirdness in the way the value is cast or returned.
Has anyone got a complete working sample of the Syncfusion grid using the CrudUrl method with MVC (not asp). I am also guessing I may have some dependency issue.
View
#Html.EJS().Grid("CrudUrl").DataSource(dataManager => { dataManager.Url("/TestGrid2/UrlDatasource").CrudUrl("/TestGrid2/CrudUpdate").Adaptor("UrlAdaptor"); }).Columns(col =>
{
col.Field("RowKey").IsPrimaryKey(true).Add();
col.Field("PartitionKey").Add();
col.Field("sourceResourceId").Add();
col.Field("imagesLocation").Add();
col.Field("imagesResourceGroup").Add();
col.Field("imagePrefix").Add();
col.Field("imageVersion").Add();
}).AllowPaging().Toolbar(new List<string>() { "Search", "Add", "Edit", "Delete", "Update", "Cancel" }).EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true); }).Render()
Controller
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Script.Services;
using System.Web.Services;
using DB;
//using Microsoft.AspNetCore.Mvc;
using Syncfusion.EJ2.Base;
namespace VMSSManagmentConsole.Controllers
{
public class TestGrid2Controller : Controller
{
private ModelContainer db = new ModelContainer();
public ActionResult TestGrid2()
{
//var items = db.ManagementItems.ToList();
//ViewBag.dataSource = items;
return View();
}
public ActionResult UrlDatasource([FromBody]DataManagerRequest dm)
{
IEnumerable DataSource = db.ManagementItems.ToList();
DataOperations operation = new DataOperations();
int count = DataSource.Cast<ManagementItem>().Count();
if (dm.Skip != 0)
{
DataSource = operation.PerformSkip(DataSource, dm.Skip); //Paging
}
if (dm.Take != 0)
{
DataSource = operation.PerformTake(DataSource, dm.Take);
}
var result = (ActionResult)(dm.RequiresCounts ? Json(new { result = DataSource, count = count }) : Json(DataSource));
return result;
}
public ActionResult CrudUpdate([FromBody]ICRUDModel<ManagementItem> value, string action)
{
//if (value.action == "update")
//{
// var ord = value.value;
// ManagementItem val = db.ManagementItems.Where(or => or.RowKey == ord.RowKey).FirstOrDefault();
// val.imagePrefix = ord.imagePrefix;
// val.imagesLocation = ord.imagesLocation;
// val.imagesResourceGroup = ord.imagesResourceGroup;
// val.imageVersion = ord.imageVersion;
// val.sourceResourceId = ord.sourceResourceId;
//}
//else if (value.action == "insert")
//{
// db.ManagementItems.Add(value.value);
//}
//else if (value.action == "remove")
//{
// db.ManagementItems.Remove(db.ManagementItems.Where(or => or.RowKey == value.key.ToString()).FirstOrDefault());
// return Json(value);
//}
//return Json(value.value);
return null;
}
public class ICRUDModel<T> where T : class
{
public string action { get; set; }
public string table { get; set; }
public string keyColumn { get; set; }
public object key { get; set; }
public T value { get; set; }
public List<T> added { get; set; }
public List<T> changed { get; set; }
public List<T> deleted { get; set; }
public IDictionary<string, object> #params { get; set; }
}
}
}
Use the DataGrid scaffold wizard. On the third page select DataSourceType = "Remote Data". A page and controller will be created with the correct code.
For your reference, we have created a sample and perform CRUD actions. Please refer the attached sample for more information.
Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/GridEJ2Mvc-914352281
The reported problem occurred when model mismatched. For Example, when we specify the number column field(EmployeeID) in grid but while inserting you did not specify the value for that column then it shows value as null in CrudUpdate, for this scenario you need to specify the nullable value for that field in model class as follows.
public class OrdersDetails
{
public OrdersDetails(int OrderID, string CustomerId, int EmployeeId, double Freight, bool Verified, DateTime OrderDate, string ShipCity, string ShipName, string ShipCountry, DateTime ShippedDate, string ShipAddress)
{
this.OrderID = OrderID;
this.CustomerID = CustomerId;
this.EmployeeID = EmployeeId;
. . . . .
}
public static List<OrdersDetails> GetAllRecords()
{
if (order.Count() == 0)
{
int code = 10000;
for (int i = 1; i < 10; i++)
{
order.Add(new OrdersDetails(code + 1, "ALFKI", i + 0, 2.3 * i, false, new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6"));
. . . . .
}
}
return order;
}
public int? OrderID { get; set; }
public string CustomerID { get; set; }
public int? EmployeeID { get; set; } // it accept null value
. . . . .
}
}
If you still face the problem then share more details or below information that will helpful for us to validate further and provide a better solution as soon as possible.
• Did the problem occurred for both update and insert?
• Share package version details.
Regards,
Thavasianand S.

asp.net mvc model state errors keys

So I discovered an interesting problem.
I have a model like this:
public class ApplicantModel
{
[Display(Name = "Firstname", ResourceType = typeof(Resources))]
[MaxLength(50, ErrorMessageResourceName = "FirstName", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
[Required(ErrorMessageResourceName = "FirstName", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
public string Firstname { get; set; }
[Display(Name = "Surname", ResourceType = typeof(Resources))]
[MaxLength(50, ErrorMessageResourceName = "Surname", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
[Required(ErrorMessageResourceName = "Surname", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
public string Surname { get; set; }
}
that is all fine, and when I check the Model state and there is an error on a model I get something like this:
errors:
[{
Key = FirstApplicant.Firstname
Value = ["First name is required field"]
},
{
Key = FirstApplicant.Surname
Value = ["Surname name is required field"]
}].
That is also fine.
Edit:
This is the c# ModelState object visualized as JSON object. Real object looks like this:
ModelState
{System.Web.Mvc.ModelStateDictionary}
Count: 2
IsReadOnly: false
IsValid: false
Keys: Count = 2
Values: Count = 2
Results View: Expanding the Results View will enumerate the IEnumerable
However my question is. Is it possible to somehow change the key? I know that the key is created as the name of object and then the name property on that object.
So it makes sense, but is there any way how to change this default behavior? Or do I have to change the names of objects?
Edit2:
What I am trying to achieve here is that I have a c# ViewModel and knockout ViewModel. and when you do server side validations you get this dictionary of keys and values which I serialize and send to client.
And then I call this function on it on client:
var errors = #Html.Raw(Json.Encode(Model.Errors));
function showErrors(serializedErrors) {
var errors = JSON.parse(serializedErrors);
for (var i = 0; i < errors.length; i++) {
var error = errors[i];
var key = error.Key;
var property = eval("masterModel." + key);
property.setError(error.Value.ErrorMessage);
property.isModified(true);
}
}
showErrors(errors);
And this would work fine if the view model property names match on the server and on client. But for example on server side I have a FirstApplicant.FirstName and on a client side it is ApplicantOne.firstname. Thank you all for help and comments. I hope I explained my problem in more detail this time.
in the end I found a solution to this problem. It is a bit complicated but it works.
First I've created an attribute.
public class ClientNameAttribute : Attribute, IMetadataAware
{
public ClientNameAttribute(string name)
{
this.Name = name;
}
public string Name { get; set; }
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues["ClientName"] = this.Name;
}
}
Notice that this attribute also implements IMetadataAware
Next step was to create Html helper, so I could call this in a view.
public static class HtmlHelperExtensions
{
public static string CustomModelState<T>(this HtmlHelper<T> helper)
{
var errors = helper.ViewData.ModelState.Select(
m => new { Key = GenerateClientName(m.Key, helper), Value = m.Value.Errors.FirstOrDefault() }).Where(e=> e.Value != null);
return Json.Encode(errors);
}
private static string GenerateClientName<T>(string key, HtmlHelper<T> helper)
{
StringBuilder builder = new StringBuilder();
int periodIndex = -1;
do
{
periodIndex = key.IndexOf('.', periodIndex + 1);
string part = key.Substring(0, periodIndex==-1 ? key.Length : periodIndex);
var partMetadata = ModelMetadata.FromStringExpression(part, helper.ViewData);
object clientName;
if (builder.Length > 0)
{
builder.Append('.');
}
if (partMetadata.AdditionalValues.TryGetValue("ClientName", out clientName))
{
builder.Append(clientName);
}
else
{
builder.Append(partMetadata.PropertyName);
}
}
while (periodIndex != -1);
return builder.ToString();
}
}
CustomModelState is a method that I call in a view.
like this:
var errors = #Html.Raw(Html.CustomModelState());
if (errors.length > 0) {
showErrors("masterModel",errors);
}
this will give you nicely formated errors, with your custom names of properties.
And here are tests for it:
public class TestModel
{
[Required]
public string Normal { get; set; }
[ClientName("Other")]
[Required]
public string Changed { get; set; }
[ClientName("Complicated")]
public TestModelTwo TestModelTwo { get; set; }
}
public class TestModelTwo
{
public string PropertyOne { get; set; }
[ClientName("Two")]
public string PropertyTwo{ get; set; }
}
[TestClass]
public class HtmlHelperExtensionsTests
{
[TestMethod]
public void CustomModelStateTests()
{
var model = new TestModel();
var page = new ViewPage();
page.ViewData.Model = model;
page.ViewData.ModelState.AddModelError("Normal", "Error1");
page.ViewData.ModelState.AddModelError("Changed", "Error2");
HtmlHelper<TestModel> helper = new HtmlHelper<TestModel>(new ViewContext(), page);
var custom = helper.CustomModelState();
string expectedResult =
"[{\"Key\":\"Normal\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error1\"}},{\"Key\":\"Other\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error2\"}}]";
Assert.AreEqual(expectedResult, custom);
}
[TestMethod]
public void CustomModelStateTests_ObjectProperty_With_ClientName()
{
var model = new TestModel();
model.TestModelTwo = new TestModelTwo();
var page = new ViewPage();
page.ViewData.Model = model;
page.ViewData.ModelState.AddModelError("TestModelTwo.PropertyOne", "Error1");
page.ViewData.ModelState.AddModelError("TestModelTwo.PropertyTwo", "Error2");
HtmlHelper<TestModel> helper = new HtmlHelper<TestModel>(new ViewContext(), page);
var custom = helper.CustomModelState();
string expectedResult =
"[{\"Key\":\"Complicated.PropertyOne\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error1\"}},{\"Key\":\"Complicated.Two\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error2\"}}]";
Assert.AreEqual(expectedResult, custom);
}
}

Generic Checkbox List View model in MVC

I want to create a generic checkbox list view model and so I got this:
public class ChckboxListViewModel<T>
{
public List<CheckboxViewModel<T>> CheckboxList { get; set; }
public IEnumerable<T> SelectedValues
{
get { return CheckboxList.Where(c => c.IsSelected).Select(c => c.Value); }
}
public ChckboxListViewModel()
{
CheckboxList = new List<CheckboxViewModel<T>>();
}
}
public class CheckboxViewModel<T>
{
public string Label { get; set; }
public T Value { get; set; }
public bool IsSelected { get; set; }
public CheckboxViewModel(string i_Label, T i_Value, bool i_IsSelected)
{
Label = i_Label;
Value = i_Value;
IsSelected = i_IsSelected;
}
}
It is used by a different view model to represent filters of different statuses:
public class FaultListFilters
{
public string SearchKeyword { get; set; }
public ChckboxListViewModel<Fault.eFaultStatus> StatusFilter { get; set; }
public FaultListFilters()
{
SearchKeyword = null;
StatusFilter = new ChckboxListViewModel<Fault.eFaultStatus>();
StatusFilter.CheckboxList.Add(new CheckboxViewModel<Fault.eFaultStatus>(FaultManagementStrings.OpenStatus,Fault.eFaultStatus.Open,true));
StatusFilter.CheckboxList.Add(new CheckboxViewModel<Fault.eFaultStatus>(FaultManagementStrings.InProgressStatus, Fault.eFaultStatus.InProgress, true));
StatusFilter.CheckboxList.Add(new CheckboxViewModel<Fault.eFaultStatus>(FaultManagementStrings.ClosedStatus, Fault.eFaultStatus.Close, false));
}
}
Now I can't find the right way to display the editors or to create an editor template for that kind of a view model because it is Generic.
I don't want o create a separate editor template for ChckboxListViewModel<int> and then another for ChckboxListViewModel<Fault.eFaultStatus> and so on..
Is it even a goose idea to use generics in this case?
Is there another way to represent and display a check-box list in MVC?
I have done the following but the modle is not binding for some reason:
#using (Html.BeginForm("FaultManagement", "Faults", FormMethod.Get, null))
{
for (int i=0 ; i<Model.FaultListFilters.StatusFilter.CheckboxList.Count() ; i++)
{
#Html.HiddenFor(m => m.FaultListFilters.StatusFilter.CheckboxList[i].Value)
#Html.CheckBoxFor(m => m.FaultListFilters.StatusFilter.CheckboxList[i].IsSelected)
#Html.LabelFor(m=> m.FaultListFilters.StatusFilter.CheckboxList[i].IsSelected,Model.FaultListFilters.StatusFilter.CheckboxList[i].Label)
}
<input type="submit" />
}
Is it even a goose idea to use generics in this case?
Don't think it is.
Is there another way to represent and display a check-box list in MVC?
I would write a custom HTML helper:
public static class HtmlExtensions
{
public static IHtmlString CheckboxListFor<TModel>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, IEnumerable<string>>> ex,
IEnumerable<string> possibleValues)
{
var metadata = ModelMetadata.FromLambdaExpression(ex, html.ViewData);
var availableValues = (IEnumerable<string>)metadata.Model;
var name = ExpressionHelper.GetExpressionText(ex);
return html.CheckboxList(name, availableValues, possibleValues);
}
private static IHtmlString CheckboxList(this HtmlHelper html, string name, IEnumerable<string> selectedValues, IEnumerable<string> possibleValues)
{
var result = new StringBuilder();
foreach (string current in possibleValues)
{
var label = new TagBuilder("label");
var sb = new StringBuilder();
var checkbox = new TagBuilder("input");
checkbox.Attributes["type"] = "checkbox";
checkbox.Attributes["name"] = name;
checkbox.Attributes["value"] = current;
var isChecked = selectedValues.Contains(current);
if (isChecked)
{
checkbox.Attributes["checked"] = "checked";
}
sb.Append(checkbox.ToString());
sb.Append(current);
label.InnerHtml = sb.ToString();
result.Append(label);
}
return new HtmlString(result.ToString());
}
}
Then you could have a view model:
public class FaultListFiltersViewModel
{
public IEnumerable<string> SelectedStatusFilters { get; set; }
public IEnumerable<string> AvailableStatusFilters
{
get
{
return new[] { "Label 1", "Label 2", "Label 3" }
}
}
}
and inside the view you could use the helper:
#Html.CheckBoxListFor(x => x.SelectedStatusFilters, Model.AvailableStatusFilters)
Here is another implementation that will better support bootstrap button-group labels (as it requires them to be seperated) and enum type selected values.
public static IHtmlString CheckboxListFor<TModel, TKey>(this HtmlHelper<TModel> helper, Expression<Func<TModel, IEnumerable<TKey>>> ex, Dictionary<TKey, string> i_PossibleOptions, object i_LabelHtmlAttributes)
where TKey : struct, IConvertible
{
var metadata = ModelMetadata.FromLambdaExpression(ex, helper.ViewData);
var selectedValues = (IEnumerable<TKey>)metadata.Model;
var name = ExpressionHelper.GetExpressionText(ex);
return helper.CheckboxList(name, selectedValues, i_PossibleOptions, i_LabelHtmlAttributes);
}
private static IHtmlString CheckboxList<TKey>(this HtmlHelper helper, string name, IEnumerable<TKey> i_SelectedValues, Dictionary<TKey, string> i_PossibleOptions, object i_LabelHtmlAttributes)
where TKey : struct, IConvertible
{
if (!typeof(TKey).IsEnum) throw new ArgumentException("T must be an enumerated type");
var result = new StringBuilder();
foreach (var option in i_PossibleOptions)
{
var label = new TagBuilder("label");
label.MergeAttributes(new RouteValueDictionary(i_LabelHtmlAttributes));
label.Attributes["for"] = string.Format("{0}",option.Key.ToString());
label.InnerHtml = option.Value;
var checkbox = new TagBuilder("input");
checkbox.Attributes["type"] = "checkbox";
checkbox.Attributes["name"] = name;
checkbox.Attributes["id"] = string.Format("{0}", option.Key.ToString());
checkbox.Attributes["value"] = option.Key.ToString();
bool isChecked = ((i_SelectedValues != null) && (i_SelectedValues.Contains(option.Key)));
if ( isChecked )
{
checkbox.Attributes["checked"] = "checked";
}
result.Append(checkbox);
result.Append(label);
}
return new HtmlString(result.ToString());
}
And then the View Model looks like that:
public class FaultListFilters
{
[Display(ResourceType = typeof(FaultManagementStrings), Name = "SearchKeyword")]
public string SearchKeyword { get; set; }
public Dictionary<Fault.eFaultStatus, string> PossibleFaultStatuses
{
get
{
var possibleFaultStatuses = new Dictionary<Fault.eFaultStatus, string>();
possibleFaultStatuses.Add(Fault.eFaultStatus.Open, FaultManagementStrings.OpenStatus);
possibleFaultStatuses.Add(Fault.eFaultStatus.InProgress, FaultManagementStrings.InProgressStatus);
possibleFaultStatuses.Add(Fault.eFaultStatus.Close, FaultManagementStrings.ClosedStatus);
return possibleFaultStatuses;
}
}
public IEnumerable<Fault.eFaultStatus> SelectedFaultStatuses { get; set; }
public FaultListFilters()
{
SearchKeyword = null;
SelectedFaultStatuses = new[] { Fault.eFaultStatus.Open, Fault.eFaultStatus.InProgress };
}
}
and the usage remains the same (except i have added the label html attributes)
<div class="btn-group">
#Html.CheckboxListFor(m => m.FaultListFilters.SelectedFaultStatuses, Model.FaultListFilters.PossibleFaultStatuses, new { Class="btn"})
</div>

Creating a list from ENUM into Model

I got the following model piece of code:
public enum EnumTest
{
[Description ("Enum Text 1")]
Value_1 = 1,
[Description ("Enum Text 2")]
Value_2 = 2,
}
public List<Fields> listFields = new List<Fields>();
public class Fields
{
public int Code { get; set;}
public string Description { get; set;}
}
I got an Enum and I would like to fill my variable CODE with enum value and the variable Description with the same enum description. I looked up a long time and failed to initialize my "ListFields" into its constructor with the enum VALUE/DESCRIPTION.
I already got the enum and the method to get its description.. I found it usefull, so I'll leave it here, maybe it can be useful for someone..
public static string GetDescription(this Enum value)
{
return (from m in value.GetType().GetMember(value.ToString())
let attr =(DescriptionAttribute)m.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault()
select attr == null ? value.ToString() : attr.Description).FirstOrDefault();
}
To use this you just need to do something like this:
String xx = Enum.EnumName.GetDescription();
You have to use reflection.
public static Fields[] GetEnumFields(Type enumType)
{
if (enumType == null)
throw new ArgumentNullException("enumType");
if (!enumType.IsEnum)
throw new ArgumentException("Not an enum");
FieldInfo[] fieldInfos = enumType.GetFields(BindingFlags.Static | BindingFlags.Public);
Fields[] result = new Fields[fieldInfos.Length];
for (int i = 0; i < fieldInfos.Length; ++i)
{
FieldInfo field = fieldInfos[i];
int value = (int)field.GetValue(null);
DescriptionAttribute attrib = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
string desc = attrib != null ? attrib.Description : field.Name;
result[i] = new Fields(value, desc);
}
return result;
}
public class Fields
{
private int value;
private string description;
public int Value
{
get { return this.value; }
}
public string Description
{
get { return this.description; }
}
public Fields(int value, string description)
{
this.value = value;
this.description = description;
}
}
To use it is quite simple:
enum test
{
[Description("hello!")]
ciao,
www
}
static void Main(string[] args)
{
foreach (Fields f in GetEnumFields(typeof(test)))
{
Console.WriteLine(f.Description);
}
}
In my implementation when a descriptionattribute is not found, field name is used.
We must also say that reflection can be slow and rebuilding the entire array when you need it is a waste of time, if you need it often.
You can store the array somewhere so you can compute it only once and keep it cached.
This of course and as I said, makes sense only if you need this readonly list very often.

attribute dependent on another field

In a model of my ASP.NET MVC application I would like validate a textbox as required only if a specific checkbox is checked.
Something like
public bool retired {get, set};
[RequiredIf("retired",true)]
public string retirementAge {get, set};
How can I do that?
Thank you.
Take a look at this: http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx
I've modded the code somewhat to suit my needs. Perhaps you benefit from those changes as well.
public class RequiredIfAttribute : ValidationAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentUpon { get; set; }
public object Value { get; set; }
public RequiredIfAttribute(string dependentUpon, object value)
{
this.DependentUpon = dependentUpon;
this.Value = value;
}
public RequiredIfAttribute(string dependentUpon)
{
this.DependentUpon = dependentUpon;
this.Value = null;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
{
public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
: base(metadata, context, attribute)
{ }
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
// no client validation - I might well blog about this soon!
return base.GetClientValidationRules();
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// get a reference to the property this validation depends upon
var field = Metadata.ContainerType.GetProperty(Attribute.DependentUpon);
if (field != null)
{
// get the value of the dependent property
var value = field.GetValue(container, null);
// compare the value against the target value
if ((value != null && Attribute.Value == null) || (value != null && value.Equals(Attribute.Value)))
{
// match => means we should try validating this field
if (!Attribute.IsValid(Metadata.Model))
// validation failed - return an error
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
}
}
Then use it:
public DateTime? DeptDateTime { get; set; }
[RequiredIf("DeptDateTime")]
public string DeptAirline { get; set; }
Just use the Foolproof validation library that is available on Codeplex:
https://foolproof.codeplex.com/
It supports, amongst others, the following "requiredif" validation attributes / decorations:
[RequiredIf]
[RequiredIfNot]
[RequiredIfTrue]
[RequiredIfFalse]
[RequiredIfEmpty]
[RequiredIfNotEmpty]
[RequiredIfRegExMatch]
[RequiredIfNotRegExMatch]
To get started is easy:
Download the package from the provided link
Add a reference to the included .dll file
Import the included javascript files
Ensure that your views references the included javascript files from within its HTML for unobtrusive javascript and jquery validation.
Using NuGet Package Manager I intstalled this: https://github.com/jwaliszko/ExpressiveAnnotations
And this is my Model:
using ExpressiveAnnotations.Attributes;
public bool HasReferenceToNotIncludedFile { get; set; }
[RequiredIf("HasReferenceToNotIncludedFile == true", ErrorMessage = "RelevantAuditOpinionNumbers are required.")]
public string RelevantAuditOpinionNumbers { get; set; }
I guarantee you this will work!
I have not seen anything out of the box that would allow you to do this.
I've created a class for you to use, it's a bit rough and definitely not flexible.. but I think it may solve your current problem. Or at least put you on the right track.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
namespace System.ComponentModel.DataAnnotations
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class RequiredIfAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "'{0}' is required";
private readonly object _typeId = new object();
private string _requiredProperty;
private string _targetProperty;
private bool _targetPropertyCondition;
public RequiredIfAttribute(string requiredProperty, string targetProperty, bool targetPropertyCondition)
: base(_defaultErrorMessage)
{
this._requiredProperty = requiredProperty;
this._targetProperty = targetProperty;
this._targetPropertyCondition = targetPropertyCondition;
}
public override object TypeId
{
get
{
return _typeId;
}
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, _requiredProperty, _targetProperty, _targetPropertyCondition);
}
public override bool IsValid(object value)
{
bool result = false;
bool propertyRequired = false; // Flag to check if the required property is required.
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
string requiredPropertyValue = (string) properties.Find(_requiredProperty, true).GetValue(value);
bool targetPropertyValue = (bool) properties.Find(_targetProperty, true).GetValue(value);
if (targetPropertyValue == _targetPropertyCondition)
{
propertyRequired = true;
}
if (propertyRequired)
{
//check the required property value is not null
if (requiredPropertyValue != null)
{
result = true;
}
}
else
{
//property is not required
result = true;
}
return result;
}
}
}
Above your Model class, you should just need to add:
[RequiredIf("retirementAge", "retired", true)]
public class MyModel
In your View
<%= Html.ValidationSummary() %>
Should show the error message whenever the retired property is true and the required property is empty.
Hope this helps.
Try my custom validation attribute:
[ConditionalRequired("retired==true")]
public string retirementAge {get, set};
It supports multiple conditions.

Resources