web-dev-qa-db-fra.com

Liaison d'une énumération à une liste déroulante dans MVC 4?

J'ai constaté un peu partout que le moyen le plus courant pour lier Enums à DropDowns est d'utiliser des méthodes d'assistance, ce qui semble un peu trop pour une tâche aussi simple en apparence.

Quel est le meilleur moyen de lier Enums à DropDownLists dans ASP.Net MVC 4?

21
Kehlan Krumme

Je pense que c'est à peu près le seul moyen (propre), ce qui est dommage, mais au moins, il existe quelques options. Je vous recommande de consulter ce blog: http://paulthecyclist.com/2013/05/24/enum-dropdown/

Désolé, c'est trop long à copier ici, mais Gist a créé une nouvelle méthode d'aide HTML pour cela.

Tout le code source est disponible sur GitHub .

19
Bernhard Hofmann

Vous pouvez à ceci:

@Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))
24
Mr. Pumpkin

Les énumérations sont supportées par le framework depuis MVC 5.1:

@Html.EnumDropDownListFor(m => m.Palette)

Le texte affiché peut être personnalisé:

public enum Palette
{
    [Display(Name = "Black & White")]
    BlackAndWhite,

    Colour
}

Lien MSDN: http://www.asp.net/mvc/overview/releases/mvc51-release-notes#Enum

13
Mihkel Müür

Dans mon contrôleur:

var feedTypeList = new Dictionary<short, string>();
foreach (var item in Enum.GetValues(typeof(FeedType)))
{
    feedTypeList.Add((short)item, Enum.GetName(typeof(FeedType), item));
}
ViewBag.FeedTypeList = new SelectList(feedTypeList, "Key", "Value", feed.FeedType);

À mon avis:

@Html.DropDownList("FeedType", (SelectList)ViewBag.FeedTypeList)
10
Kehlan Krumme

Techniquement, vous n'avez pas besoin de méthode d'assistance, car Html.DropdownListFor requiert uniquement une variable SelectList ou Ienumerable<SelectListItem>. Vous pouvez simplement transformer vos enums en une telle sortie et l'alimenter de cette façon.

J'utilise une méthode de bibliothèque statique pour convertir des enums en List<SelectListItem> avec quelques paramètres/options:

public static List<SelectListItem> GetEnumsByType<T>(bool useFriendlyName = false, List<T> exclude = null,
    List<T> eachSelected = null, bool useIntValue = true) where T : struct, IConvertible
{
    var enumList = from enumItem in EnumUtil.GetEnumValuesFor<T>()
                    where (exclude == null || !exclude.Contains(enumItem))
                    select enumItem;

    var list = new List<SelectListItem>();

    foreach (var item in enumList)
    {
        var selItem = new SelectListItem();

        selItem.Text = (useFriendlyName) ? item.ToFriendlyString() : item.ToString();
        selItem.Value = (useIntValue) ? item.To<int>().ToString() : item.ToString();

        if (eachSelected != null && eachSelected.Contains(item))
            selItem.Selected = true;

        list.Add(selItem);
    }

    return list;
}

public static class EnumUtil
{
    public static IEnumerable<T> GetEnumValuesFor<T>()
    {
        return Enum.GetValues(typeof(T)).Cast<T>();
    }
    // other stuff in here too...
}


/// <summary>
/// Turns Camelcase or underscore separated phrases into properly spaces phrases
/// "DogWithMustard".ToFriendlyString() == "Dog With Mustard"
/// </summary>
public static string ToFriendlyString(this object o)
{
    var s = o.ToString();
    s = s.Replace("__", " / ").Replace("_", " ");

    char[] origArray = s.ToCharArray();
    List<char> newCharList = new List<char>();

    for (int i = 0; i < origArray.Count(); i++)
    {
        if (origArray[i].ToString() == origArray[i].ToString().ToUpper())
        {
            newCharList.Add(' ');
        }
        newCharList.Add(origArray[i]);
    }

    s = new string(newCharList.ToArray()).TrimStart();
    return s;
}

Votre ViewModel peut transmettre les options de votre choix. En voici un assez complexe:

public IEnumerable<SelectListItem> PaymentMethodChoices 
{ 
    get 
    { 
        var exclusions = new List<Membership.Payment.PaymentMethod> { Membership.Payment.PaymentMethod.Unknown, Membership.Payment.PaymentMethod.Reversal };
        var selected = new List<Membership.Payment.PaymentMethod> { this.SelectedPaymentMethod };
        return GetEnumsByType<Membership.Payment.PaymentMethod>(useFriendlyName: true, exclude: exclusions, eachSelected: selected); 
    }
}

Donc, vous câblez la variable DropDownList de votre View avec cette propriété IEnumerable<SelectListItem>.

4
Graham

La solution de PaulTheCyclist est parfaite. Mais je ne voudrais pas utiliser RESX (je devrais ajouter un nouveau fichier .resx pour chaque nouvelle énumération ??)

Voici mon expression HtmlHelper:

public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TEnum>> expression, object attributes = null)
{
    //Get metadata from enum
    var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
    var enumType = GetNonNullableModelType(metadata);
    var values = Enum.GetValues(enumType).Cast<TEnum>();

    //Convert enumeration items into SelectListItems
    var items =
        from value in values
        select new SelectListItem
        {
            Text = value.ToDescription(),
            Value = value.ToString(),
            Selected = value.Equals(metadata.Model)
        };

    //Check for nullable value types
    if (metadata.IsNullableValueType)
    {
        var emptyItem = new List<SelectListItem>
        {
            new SelectListItem {Text = string.Empty, Value = string.Empty}
        };
        items = emptyItem.Concat(items);
    }

    //Return the regular DropDownlist helper
    return htmlHelper.DropDownListFor(expression, items, attributes);
}

Voici comment je déclare mes enums:

[Flags]
public enum LoanApplicationType
{
    [Description("Undefined")]
    Undefined = 0,

    [Description("Personal Loan")]
    PersonalLoan = 1,

    [Description("Mortgage Loan")]
    MortgageLoan = 2,

    [Description("Vehicle Loan")]
    VehicleLoan = 4,

    [Description("Small Business")]
    SmallBusiness = 8,
}

Et voici l'appel d'une vue rasoir:

<div class="control-group span2">
    <div class="controls">
        @Html.EnumDropDownListFor(m => m.LoanType, new { @class = "span2" })
    </div>
</div>

@Model.LoanType est une propriété de modèle du type LoanApplicationType

UPDATE: Désolé, j'ai oublié d'inclure le code de la fonction d'assistance ToDescription ()

/// <summary>
/// Returns Description Attribute information for an Enum value
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToDescription(this Enum value)
{
    if (value == null)
    {
        return string.Empty;
    }
    var attributes = (DescriptionAttribute[]) value.GetType().GetField(
        Convert.ToString(value)).GetCustomAttributes(typeof (DescriptionAttribute), false);
    return attributes.Length > 0 ? attributes[0].Description : Convert.ToString(value);
}
4
amhed

L'extension de l'assistant HTML pour le faire fonctionne bien, mais si vous souhaitez pouvoir modifier le texte des valeurs déroulantes en fonction des mappages DisplayAttribute, vous devrez le modifier de la même manière,

(Faites ceci avant MVC 5.1, il est inclus dans 5.1+)

public static IHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> html, Expression<Func<TModel, TEnum>> expression)
{
    var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
    var enumType = Nullable.GetUnderlyingType(metadata.ModelType) ?? metadata.ModelType;
    var enumValues = Enum.GetValues(enumType).Cast<object>();
    var items = enumValues.Select(item =>
    {
        var type = item.GetType();
        var member = type.GetMember(item.ToString());
        var attribute = member[0].GetCustomAttribute<DisplayAttribute>();
        string text = attribute != null ? ((DisplayAttribute)attribute).Name : item.ToString();
        string value = ((int)item).ToString();
        bool selected = item.Equals(metadata.Model);
        return new SelectListItem
        {
            Text = text,
            Value = value,
            Selected = selected
        };
    });
    return html.DropDownListFor(expression, items, string.Empty, null);
}
0
Ryan Mann