MVC3 Razor DropDownListFor Enums

84

Essayer de mettre à jour mon projet vers MVC3, quelque chose que je ne trouve tout simplement pas:

J'ai un type de données simple ENUMS:

public enum States()
{
  AL,AK,AZ,...WY
}

Ce que je veux utiliser comme DropDown / SelectList dans ma vue d'un modèle qui contient ce type de données:

public class FormModel()
{
    public States State {get; set;}
}

Assez simple: quand je vais utiliser la vue de génération automatique pour cette classe partielle, elle ignore ce type.

J'ai besoin d'une liste de sélection simple qui définit la valeur de l'énumération comme élément sélectionné lorsque je clique sur Soumettre et traite via ma méthode AJAX - JSON POST.

Et que la vue (???!):

    <div class="editor-field">
        @Html.DropDownListFor(model => model.State, model => model.States)
    </div>

Merci à l'avance pour les conseils!

jordan.baucke
la source
8
Pour toute personne qui rencontre ce fil et utilise MVC 5.1 ou supérieur, la méthode d'assistance @ Html.EnumDropDownListFor () est désormais intégrée à MVC - voir asp.net/mvc/overview/releases/mvc51-release-notes
mecsco

Réponses:

55

Je viens d'en créer un pour mon propre projet. Le code ci-dessous fait partie de ma classe d'assistance, j'espère avoir toutes les méthodes nécessaires. Écrivez un commentaire si cela ne fonctionne pas et je vérifierai à nouveau.

public static class SelectExtensions
{

    public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
    {
        if (expression.Body.NodeType == ExpressionType.Call)
        {
            MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
            string name = GetInputName(methodCallExpression);
            return name.Substring(expression.Parameters[0].Name.Length + 1);

        }
        return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
    }

    private static string GetInputName(MethodCallExpression expression)
    {
        // p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw...
        MethodCallExpression methodCallExpression = expression.Object as MethodCallExpression;
        if (methodCallExpression != null)
        {
            return GetInputName(methodCallExpression);
        }
        return expression.Object.ToString();
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) where TModel : class
    {
        string inputName = GetInputName(expression);
        var value = htmlHelper.ViewData.Model == null
            ? default(TProperty)
            : expression.Compile()(htmlHelper.ViewData.Model);

        return htmlHelper.DropDownList(inputName, ToSelectList(typeof(TProperty), value.ToString()));
    }

    public static SelectList ToSelectList(Type enumType, string selectedItem)
    {
        List<SelectListItem> items = new List<SelectListItem>();
        foreach (var item in Enum.GetValues(enumType))
        {
            FieldInfo fi = enumType.GetField(item.ToString());
            var attribute = fi.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
            var title = attribute == null ? item.ToString() : ((DescriptionAttribute)attribute).Description;
            var listItem = new SelectListItem
                {
                    Value = ((int)item).ToString(),
                    Text = title,
                    Selected = selectedItem == ((int)item).ToString()
                };
            items.Add(listItem);
        }

        return new SelectList(items, "Value", "Text", selectedItem);
    }
}

Utilisez-le comme:

Html.EnumDropDownListFor(m => m.YourEnum);

Mise à jour

J'ai créé des Helpers Html alternatifs. Tout ce que vous devez faire pour les utiliser est de changer votre page de base views\web.config.

Avec eux, vous pouvez simplement faire:

@Html2.DropDownFor(m => m.YourEnum);
@Html2.CheckboxesFor(m => m.YourEnum);
@Html2.RadioButtonsFor(m => m.YourEnum);

Plus d'infos ici: http://blog.gauffin.org/2011/10/first-draft-of-my-alternative-html-helpers/

Jgauffin
la source
1
Ok bien cela fonctionne de toute façon, je reçois juste une erreur de compilation: Ligne 41: return htmlHelper.DropDownList (inputName, ToSelectList (typeof (TProperty), value.ToString ())); 'System.Web.Mvc.HtmlHelper <TModel>' ne contient pas de définition pour 'DropDownList' et aucune méthode d'extension 'DropDownList' acceptant un premier argument de type 'System.Web.Mvc.HtmlHelper <TModel>' n'a pu être trouvée ( vous manque une directive using ou une référence d'assemblage?)
jordan.baucke
1
@jordan J'ai la même erreur. Avez-vous réussi à résoudre le problème?
SF Developer
9
@filu @jordan ajoutez using System.Web.Mvc.Html;comme vous devez accéder auSelectExtensionsClass
Simon Hartcher
3
@Para J'obtiens le même problème, la valeur sélectionnée n'apparaît pas sélectionnée dans la vue. (J'ai dû changer ((int)item).ToString()pour Enum.GetName(enumType, item)obtenir le SelectListItemcorrectement enregistré comme sélectionné, mais cela ne fonctionne toujours pas)
Fernando Neira
1
Je viens d'ajouter une réponse ci-dessous qui couvre le problème de sélection - découle d'une mauvaise compréhension des comportements des surcharges DropDownList.
Jon Egerton
199

J'ai trouvé une solution plus simple pour cela ici: http://coding-in.net/asp-net-mvc-3-method-extension/

using System;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace EnumHtmlHelper.Helper
{    
    public static class EnumDropDownList
    {
        public static HtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> modelExpression, string firstElement)
        {
            var typeOfProperty = modelExpression.ReturnType;
            if(!typeOfProperty.IsEnum)
                throw new ArgumentException(string.Format("Type {0} is not an enum", typeOfProperty));     
            var enumValues = new SelectList(Enum.GetValues(typeOfProperty));
            return htmlHelper.DropDownListFor(modelExpression, enumValues, firstElement);
}   }   }

Une ligne de rasoir le fera:

@Html.DropDownListFor(model => model.State, new SelectList(Enum.GetValues(typeof(MyNamespace.Enums.States))))

Vous pouvez également trouver du code pour le faire avec une méthode d'extension dans l'article lié.

Mike McLaughlin
la source
6
Je pense que celui-ci aurait dû être marqué comme la solution. Le meilleur n'est pas marqué par la complexité mais par la simplicité.
Lord of Scripts
3
Pour les personnes qui recherchent une version DropDowList (comme moi): @ Html.DropDownList ("listName", new SelectList (Enum.GetValues ​​(typeof (MyNamespace.Enums.States))))
dstr
2
@JonEgerton Si vous voulez dire la même chose que moi, je suis d'accord. Si vous voulez afficher des énumérations + une description + une image, vous êtes perdu avec la solution de Mike McLaughlin.
Elisabeth
1
Le seul problème que j'ai trouvé avec cette solution est qu'elle ne mappe pas correctement la valeur sélectionnée lors du chargement. A part ça, plutôt bien.
triangulito
3
@triangulito ce n'est pas du tout un problème :)@Html.DropDownListFor(model => model.State, new SelectList(Enum.GetValues(typeof(MyNamespace.Enums.States)),model.State))
VladL
24

À partir d' ASP.NET MVC 5.1 (RC1) , EnumDropDownListForest inclus par défaut en tant que méthode d'extension de HtmlHelper.

Vincent Sels
la source
17

Si vous voulez quelque chose de vraiment simple, il existe un autre moyen, en fonction de la façon dont vous stockez l'état dans la base de données.

Si vous aviez une entité comme celle-ci:

public class Address
{
     //other address fields

     //this is what the state gets stored as in the db
     public byte StateCode { get; set; }

     //this maps our db field to an enum
     public States State
     {
         get
         {
             return (States)StateCode;
         }
         set
         {
             StateCode = (byte)value;
         }
     }
}

Ensuite, générer la liste déroulante serait aussi simple que ceci:

@Html.DropDownListFor(x => x.StateCode,
    from State state in Enum.GetValues(typeof(States))
    select new SelectListItem() { Text = state.ToString(), Value = ((int)state).ToString() }
);

LINQ n'est-il pas joli?

sjmeverett
la source
où définissez-vous l'énumération des états dans le modèle ou la vue?
superartsy
dans le modèle, tel qu'il est utilisé par la classe modèle
sjmeverett
1
@stewartml Lorsque mon ViewModel a la propriété enum + le "SelectedCodeProperty", c'est une propriété de trop dans votre message. Pourquoi ne pas avoir l'énumération dans les deux en tant que valeur sélectionnée postée sur le serveur + comme valeur d'élément.
Elisabeth
13

J'ai pu le faire en une seule ligne.

@Html.DropDownListFor(m=>m.YourModelProperty,new SelectList(Enum.GetValues(typeof(YourEnumType))))
JM1990
la source
8

Sur la base de la réponse acceptée par @jgauffin, j'ai créé ma propre version de EnumDropDownListFor, qui traite du problème de la sélection des éléments.

Le problème est détaillé dans une autre réponse SO ici:, et est essentiellement dû à une mauvaise compréhension du comportement des différentes surcharges de DropDownList.

Mon code complet (qui inclut les surcharges pour htmlAttributesetc est:

public static class EnumDropDownListForHelper
{

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, null, null);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            object htmlAttributes
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, null, htmlAttributes);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            IDictionary<string, object> htmlAttributes
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, null, htmlAttributes);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            string optionLabel
        ) where TModel : class
    {
        return EnumDropDownListFor<TModel, TProperty>(
                            htmlHelper, expression, optionLabel, null);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            string optionLabel, 
            IDictionary<string,object> htmlAttributes
        ) where TModel : class
    {
        string inputName = GetInputName(expression);
        return htmlHelper.DropDownList(
                            inputName, ToSelectList(typeof(TProperty)), 
                            optionLabel, htmlAttributes);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression, 
            string optionLabel, 
            object htmlAttributes
        ) where TModel : class
    {
        string inputName = GetInputName(expression);
        return htmlHelper.DropDownList(
                            inputName, ToSelectList(typeof(TProperty)), 
                            optionLabel, htmlAttributes);
    }


    private static string GetInputName<TModel, TProperty>(
            Expression<Func<TModel, TProperty>> expression)
    {
        if (expression.Body.NodeType == ExpressionType.Call)
        {
            MethodCallExpression methodCallExpression 
                            = (MethodCallExpression)expression.Body;
            string name = GetInputName(methodCallExpression);
            return name.Substring(expression.Parameters[0].Name.Length + 1);

        }
        return expression.Body.ToString()
                    .Substring(expression.Parameters[0].Name.Length + 1);
    }

    private static string GetInputName(MethodCallExpression expression)
    {
        // p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw...
        MethodCallExpression methodCallExpression 
                            = expression.Object as MethodCallExpression;
        if (methodCallExpression != null)
        {
            return GetInputName(methodCallExpression);
        }
        return expression.Object.ToString();
    }


    private static SelectList ToSelectList(Type enumType)
    {
        List<SelectListItem> items = new List<SelectListItem>();
        foreach (var item in Enum.GetValues(enumType))
        {
            FieldInfo fi = enumType.GetField(item.ToString());
            var attribute = fi.GetCustomAttributes(
                                       typeof(DescriptionAttribute), true)
                                  .FirstOrDefault();
            var title = attribute == null ? item.ToString() 
                              : ((DescriptionAttribute)attribute).Description;
            var listItem = new SelectListItem
            {
                Value = item.ToString(),
                Text = title,
            };
            items.Add(listItem);
        }

        return new SelectList(items, "Value", "Text");
    }
}

J'ai écrit ceci sur mon blog ici .

Jon Egerton
la source
1
C'est la seule solution que j'ai rencontrée qui présélectionne correctement la valeur appropriée pour mon énumération. Merci!
Edwin Groenendaal
Impressionnant. Cela devrait certainement être la réponse acceptée - cela fonctionne; la réponse acceptée ne l'est pas.
neminem
3

Cela serait utile pour sélectionner une valeur int à partir de enum: Voici SpecTypeun intchamp ... et enmSpecTypeest un enum.

@Html.DropDownList(
    "SpecType", 
     YourNameSpace.SelectExtensions.ToSelectList(typeof(NREticaret.Core.Enums.enmSpecType), 
     Model.SpecType.ToString()), "Tip Seçiniz", new 
     { 
         gtbfieldid = "33", 
         @class = "small" 
     })
user687314
la source
3

J'ai apporté la modification suivante à la méthode SelectList pour qu'elle fonctionne un peu mieux pour moi. Peut-être que ce sera utile pour les autres.

public static SelectList ToSelectList<T>(T selectedItem)
        {
            if (!typeof(T).IsEnum) throw new InvalidEnumArgumentException("The specified type is not an enum");

            var selectedItemName = Enum.GetName(typeof (T), selectedItem);
            var items = new List<SelectListItem>();
            foreach (var item in Enum.GetValues(typeof(T)))
            {
                var fi = typeof(T).GetField(item.ToString());
                var attribute = fi.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();

                var enumName = Enum.GetName(typeof (T), item);
                var title = attribute == null ? enumName : ((DescriptionAttribute)attribute).Description;

                var listItem = new SelectListItem
                {
                    Value = enumName,
                    Text = title,
                    Selected = selectedItemName == enumName
                };
                items.Add(listItem);
            }

            return new SelectList(items, "Value", "Text");
        }
Jason
la source
3
    public enum EnumStates
    {
        AL = 0,
        AK = 1,
        AZ = 2,
        WY = 3
    }


@Html.DropDownListFor(model => model.State, (from EnumStates e in Enum.GetValues(typeof(EnumStates))
                                                               select new SelectListItem { Value = ((int)e).ToString(), Text = e.ToString() }), "select", new { @style = "" })
                @Html.ValidationMessageFor(model => model.State)  //With select



//Or


@Html.DropDownListFor(model => model.State, (from EnumStates e in Enum.GetValues(typeof(EnumStates))
                                                               select new SelectListItem { Value = ((int)e).ToString(), Text = e.ToString() }), null, new { @style = "" })
                @Html.ValidationMessageFor(model => model.State)   //With out select
Thulasiram
la source
où définissez-vous EnumState?
superartsy
in top u can see it ... public enum EnumStates
Thulasiram
2

Identique à celui de Mike (qui est enterré entre de longues réponses)

model.truckimagelocation est la propriété d'instance de classe du type d'énumération TruckImageLocation

@Html.DropDownListFor(model=>model.truckimagelocation,Enum.GetNames(typeof(TruckImageLocation)).ToArray().Select(f=> new SelectListItem() {Text = f, Value = f, Selected = false}))
user794791
la source
2

C'est le code le plus générique qui sera utilisé pour tous les Enums.

public static class UtilitiesClass
{

    public static SelectList GetEnumType(Type enumType)
    {
        var value = from e in Enum.GetNames(enumType)
                    select new
                    {
                        ID = Convert.ToInt32(Enum.Parse(enumType, e, true)),
                        Name = e
                    };
        return new SelectList(value, "ID", "Name");
    }
}

Méthode d'action

ViewBag.Enum= UtilitiesClass.GetEnumType(typeof (YourEnumType));

View.cshtml

 @Html.DropDownList("Type", (IEnumerable<SelectListItem>)ViewBag.Enum, new { @class = "form-control"})
Muhammad Kamran
la source
1

vous pouvez utiliser enum dans votre modèle

votre Enum

public enum States()
{
  AL,AK,AZ,...WY
}

faire un modèle

public class enumclass
{
public States statesprop {get; set;}
}

en vue

@Html.Dropdownlistfor(a=>a.statesprop)
MaTya
la source
Dernières questions Répondez à kar.
Anup
1

La réponse la plus simple dans MVC5 est Define Enum:

public enum ReorderLevels {
          zero = 0,
            five = 5,
            ten = 10,
            fifteen = 15,
            twenty = 20,
            twenty_five = 25,
            thirty = 30
        }

Lier dans la vue:

        <div class="form-group">
            <label>Reorder Level</label>
            @Html.EnumDropDownListFor(m => m.ReorderLevel, "Choose Me", new { @class = "form-control" })
        </div>
Mark Phillips
la source