121 lines
5.4 KiB
C#
121 lines
5.4 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Diagnostics.Contracts;
|
|
using System.Reflection;
|
|
|
|
namespace System.Web.Mvc.Html
|
|
{
|
|
public static class EnumHelper
|
|
{
|
|
/// <summary>
|
|
/// Gets a value indicating whether the given <paramref name="type"/> or an expression of this
|
|
/// <see cref="Type"/> is suitable for use in <see cref="GetSelectList(Type)"/> and <see
|
|
/// cref="SelectExtensions.EnumDropDownListFor{TModel,TEnum}(HtmlHelper{TModel}, Expression{Func{TModel, TEnum}})"/>
|
|
/// calls.
|
|
/// </summary>
|
|
/// <param name="type">The <see cref="Type"/> to check.</param>
|
|
/// <returns>
|
|
/// <see langword="true"/> if <see cref="GetSelectList(Type)"/> will not throw when passed given
|
|
/// <see cref="Type"/> and <see
|
|
/// cref="SelectExtensions.EnumDropDownListFor{TModel,TEnum}(HtmlHelper{TModel}, Expression{Func{TModel, TEnum}})"/>
|
|
/// will not throw when passed an expression of this <see cref="Type"/>; <see langword="false"/> otherwise.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// Currently returns <see langref="true"/> if the <paramref name="type"/> parameter is
|
|
/// non-<see langref="null"/>, is an <see langref="enum"/> type, and does not have a
|
|
/// <see cref="FlagsAttribute"/> attribute.
|
|
/// </remarks>
|
|
public static bool IsValidForEnumHelper(Type type)
|
|
{
|
|
bool isValid = false;
|
|
if (type != null)
|
|
{
|
|
// Type.IsEnum is false for Nullable<T> even if T is an enum. Check underlying type (if any).
|
|
// Do not support Enum type itself -- IsEnum property is false for that class.
|
|
Type checkedType = Nullable.GetUnderlyingType(type) ?? type;
|
|
if (checkedType.IsEnum)
|
|
{
|
|
isValid = !HasFlagsInternal(checkedType);
|
|
}
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of <see cref="SelectListItem"/> objects corresponding to enum constants defined in the given
|
|
/// <paramref name="type"/>.
|
|
/// </summary>
|
|
/// <param name="type">The <see cref="Type"/> to evaluate.</param>
|
|
/// <returns> An <see cref="IList{SelectListItem}"/> for the given <paramref name="type"/>.</returns>
|
|
/// <remarks>
|
|
/// Throws if <see cref="IsValidForEnumHelper(Type)"/> returns <see langref="false"/> for the given
|
|
/// <paramref name="type"/>.
|
|
/// </remarks>
|
|
public static IList<SelectListItem> GetSelectList(Type type)
|
|
{
|
|
if (type == null)
|
|
{
|
|
throw new ArgumentNullException("type");
|
|
}
|
|
|
|
if (!IsValidForEnumHelper(type))
|
|
{
|
|
throw new ArgumentException("非枚举类型,不支持", "type");
|
|
}
|
|
|
|
IList<SelectListItem> selectList = new List<SelectListItem>();
|
|
|
|
// According to HTML5: "The first child option element of a select element with a required attribute and
|
|
// without a multiple attribute, and whose size is "1", must have either an empty value attribute, or must
|
|
// have no text content." SelectExtensions.DropDownList[For]() methods often generate a matching
|
|
// <select/>. Empty value for Nullable<T>, empty text for round-tripping an unrecognized value, or option
|
|
// label serves in some cases. But otherwise, ignoring this does not cause problems in either IE or Chrome.
|
|
Type checkedType = Nullable.GetUnderlyingType(type) ?? type;
|
|
if (checkedType != type)
|
|
{
|
|
// Underlying type was non-null so handle Nullable<T>; ensure returned list has a spot for null
|
|
selectList.Add(new SelectListItem { Text = String.Empty, Value = String.Empty, });
|
|
}
|
|
|
|
// Populate the list
|
|
const BindingFlags BindingFlags =
|
|
BindingFlags.DeclaredOnly | BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static;
|
|
foreach (FieldInfo field in checkedType.GetFields(BindingFlags))
|
|
{
|
|
// fieldValue will be an numeric type (byte, ...)
|
|
object fieldValue = field.GetRawConstantValue();
|
|
|
|
selectList.Add(new SelectListItem { Text = GetDisplayName(field), Value = fieldValue.ToString(), });
|
|
}
|
|
|
|
return selectList;
|
|
}
|
|
|
|
private static bool HasFlagsInternal(Type type)
|
|
{
|
|
Contract.Assert(type != null);
|
|
|
|
FlagsAttribute attribute = type.GetCustomAttribute<FlagsAttribute>(inherit: false);
|
|
return attribute != null;
|
|
}
|
|
|
|
// Return non-empty name specified in a [Display] attribute for the given field, if any; field's name otherwise
|
|
private static string GetDisplayName(FieldInfo field)
|
|
{
|
|
DisplayAttribute display = field.GetCustomAttribute<DisplayAttribute>(inherit: false);
|
|
if (display != null)
|
|
{
|
|
string name = display.GetName();
|
|
if (!String.IsNullOrEmpty(name))
|
|
{
|
|
return name;
|
|
}
|
|
}
|
|
|
|
return field.Name;
|
|
}
|
|
}
|
|
} |