可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a list of enums that I am using for a user management page. I'm using the new HtmlHelper in MVC 5.1 that allows me to create a dropdown list for Enum values. I now have a need to remove the Pending value from the list, this value will only ever be set programatically and should never be set by the user.
Enum:
public enum UserStatus
{
Pending = 0,
Limited = 1,
Active = 2
}
View:
@Html.EnumDropDownListFor(model => model.Status)
Is there anyway, either overriding the current control, or writing a custom HtmlHelper that would allow me to specify an enum, or enums to exclude from the resulting list? Or would you suggest I do something client side with jQuery to remove the value from the dropdown list once it has been generated?
Thanks!
回答1:
You could construct a drop down list:
@{ // you can put the following in a back-end method and pass through ViewBag
var selectList = Enum.GetValues(typeof(UserStatus))
.Cast<UserStatus>()
.Where(e => e != UserStatus.Pending)
.Select(e => new SelectListItem
{
Value = ((int)e).ToString(),
Text = e.ToString()
});
}
@Html.DropDownListFor(m => m.Status, selectList)
回答2:
Modified from @dav_i's answer.
This is not perfect, but it is what I am using. Below is an extension to HtmlHelper
. The extension method will look like EnumDropDownListFor
from ASP.NET, and use DisplayAttribute
if there is any applied to the Enum value.
/// <summary>
/// Returns an HTML select element for each value in the enumeration that is
/// represented by the specified expression and predicate.
/// </summary>
/// <typeparam name="TModel">The type of the model.</typeparam>
/// <typeparam name="TEnum">The type of the value.</typeparam>
/// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
/// <param name="expression">An expression that identifies the object that contains the properties to display.</param>
/// <param name="optionLabel">The text for a default empty item. This parameter can be null.</param>
/// <param name="predicate">A <see cref="Func{TEnum, bool}"/> to filter the items in the enums.</param>
/// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
/// <returns>An HTML select element for each value in the enumeration that is represented by the expression and the predicate.</returns>
/// <exception cref="ArgumentNullException">If expression is null.</exception>
/// <exception cref="ArgumentException">If TEnum is not Enum Type.</exception>
public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, Func<TEnum, bool> predicate, string optionLabel, object htmlAttributes) where TEnum : struct, IConvertible
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
if (!typeof(TEnum).IsEnum)
{
throw new ArgumentException("TEnum");
}
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
IList<SelectListItem> selectList = Enum.GetValues(typeof(TEnum))
.Cast<TEnum>()
.Where(e => predicate(e))
.Select(e => new SelectListItem
{
Value = Convert.ToUInt64(e).ToString(),
Text = ((Enum)(object)e).GetDisplayName(),
}).ToList();
if (!string.IsNullOrEmpty(optionLabel)) {
selectList.Insert(0, new SelectListItem {
Text = optionLabel,
});
}
return htmlHelper.DropDownListFor(expression, selectList, htmlAttributes);
}
/// <summary>
/// Gets the name in <see cref="DisplayAttribute"/> of the Enum.
/// </summary>
/// <param name="enumeration">A <see cref="Enum"/> that the method is extended to.</param>
/// <returns>A name string in the <see cref="DisplayAttribute"/> of the Enum.</returns>
public static string GetDisplayName(this Enum enumeration)
{
Type enumType = enumeration.GetType();
string enumName = Enum.GetName(enumType, enumeration);
string displayName = enumName;
try
{
MemberInfo member = enumType.GetMember(enumName)[0];
object[] attributes = member.GetCustomAttributes(typeof(DisplayAttribute), false);
DisplayAttribute attribute = (DisplayAttribute)attributes[0];
displayName = attribute.Name;
if (attribute.ResourceType != null)
{
displayName = attribute.GetName();
}
}
catch { }
return displayName;
}
For example:
@Html.EnumDropDownListFor(
model => model.UserStatus,
(userStatus) => { return userStatus != UserStatus.Active; },
null,
htmlAttributes: new { @class = "form-control" });
This will create a Enum dropdown list without the the option of Active.
回答3:
You can create the dropdown yourself by looping through the values in the enum and only include the <option>
if it is not Pending.
Here is how it should work, but as you can see, I'm not sure what you would use for the value or text of the option tag.
<select>
foreach (var status in Enum.GetValues(typeof(UserStatus)))
{
if(status != UserStatus.Pending)
{
<option value="status.???">@status.???</option>
}
}
</select>
回答4:
Below is an extension to the HtmlHelper. It is very similar to the EnumDropDownListFor extension from ASP.NET, but it sorts the SelectListItem by the item display name. It has a suggestive name: SortedEnumDropDownListFor for not conflicts with the original extension.
/// <summary>
///
/// </summary>
/// <typeparam name="TModel">The type of the model.</typeparam>
/// <typeparam name="TEnum">The type of the value.</typeparam>
/// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
/// <param name="expression">An expression that identifies the object that contains the properties to display</param>
/// <param name="initalValue">The unselected item initial value</param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static MvcHtmlString SortedEnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, string initalValue, object htmlAttributes = null)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
Type enumType = GetNonNullableModelType(metadata);
Type baseEnumType = Enum.GetUnderlyingType(enumType);
List<SelectListItem> items = new List<SelectListItem>();
foreach (FieldInfo field in enumType.GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public))
{
string text = field.Name;
string value = Convert.ChangeType(field.GetValue(null), baseEnumType).ToString();
bool selected = field.GetValue(null).Equals(metadata.Model);
foreach (DisplayAttribute displayAttribute in field.GetCustomAttributes(true).OfType<DisplayAttribute>())
{
text = displayAttribute.GetName();
}
items.Add(new SelectListItem
{
Text = text,
Value = value,
Selected = selected
});
}
items = new List<SelectListItem>(items.OrderBy(s => s.Text));
items.Insert(0, new SelectListItem { Text = initalValue, Value = "" });
return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
}
private static Type GetNonNullableModelType(ModelMetadata modelMetadata)
{
Type realModelType = modelMetadata.ModelType;
Type underlyingType = Nullable.GetUnderlyingType(realModelType);
if (underlyingType != null)
{
realModelType = underlyingType;
}
return realModelType;
}
If you don't want to bother with the unselected item intitia, just build a overload like this:
public static MvcHtmlString SortedEnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes = null)
{
MvcHtmlString helper = SortedEnumDropDownListFor(htmlHelper, expression, string.Empty, htmlAttributes);
return helper;
}
And you are good to go. I hope it helps.
回答5:
I was looking for the answer to this question as it relates to .NET Core MVC. Given the following code, you can limit the enums you do not want to display within the UI:
<select asp-for="UserStatus" asp-items="@(Html.GetEnumSelectList<UserStatus>().Where(x => x.Value != 0))" class="form-control">
<option selected="selected" value="">Please select</option>
</select>
Hope this helps anyone else looking for this answer.