it-swarm.com.de

Binden eines Enums an eine DropDownList in MVC 4?

Ich habe überall festgestellt, dass die übliche Methode, Enums an DropDowns zu binden, durch Hilfsmethoden erfolgt, die für eine so scheinbar einfache Aufgabe etwas anstrengend erscheinen.

Was ist der beste Weg, um Enums an DropDownLists in ASP.Net MVC 4 zu binden?

21
Kehlan Krumme

Ich denke, es handelt sich um den einzigen (sauberen) Weg, was schade ist, aber es gibt zumindest ein paar Möglichkeiten. Ich würde empfehlen, einen Blick auf diesen Blog zu werfen: http://paulthecyclist.com/2013/05/24/enum-dropdown/

Tut mir leid, es ist zu lang, um es hier zu kopieren, aber der Gist ist, dass er dafür eine neue HTML-Helper-Methode erstellt hat.

Der gesamte Quellcode ist auf GitHub verfügbar.

19

Sie können dazu:

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

Enumerationen werden seit MVC 5.1 vom Framework unterstützt:

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

Angezeigter Text kann angepasst werden:

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

    Colour
}

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

13
Mihkel Müür

In meinem Controller:

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);

Aus meiner Sicht:

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

Technisch benötigen Sie keine Hilfsmethode, da Html.DropdownListFor nur eine SelectList oder Ienumerable<SelectListItem> benötigt. Sie können Ihr Enum einfach in eine solche Ausgabe umwandeln und auf diese Weise füttern.

Ich verwende eine statische Bibliotheksmethode, um Enummen mit ein paar Parametern/Optionen in List<SelectListItem> zu konvertieren:

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;
}

Ihr ViewModel kann die gewünschten Optionen übergeben. Hier ist ein ziemlich komplexer:

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); 
    }
}

Sie verbinden also die DropDownList Ihrer View mit dieser IEnumerable<SelectListItem>-Eigenschaft.

4
Graham

Die Lösung von PaulTheCyclist ist genau richtig. Aber ich würde RESX nicht verwenden (ich müsste für jede neue Enumeration eine neue RESX-Datei hinzufügen?)

Hier ist mein HtmlHelper-Ausdruck:

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);
}

So erkläre ich mein Enum:

[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,
}

Und hier ist der Anruf von einer Rasiermesseransicht:

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

Dabei ist @Model.LoanType eine Modelleigenschaft des Typs LoanApplicationType

UPDATE: Entschuldigung, ich habe vergessen, Code für die Hilfsfunktion ToDescription () anzugeben.

/// <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

Das Erweitern des HTML-Helpers zu diesem Zweck funktioniert gut. Wenn Sie jedoch den Text der Dropdown-Werte basierend auf DisplayAttribute-Zuordnungen ändern möchten, müssen Sie ihn ähnlich wie

(Tun Sie dies vor MVC 5.1, es ist in 5.1+ enthalten.)

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