MVC3 Razor DropDownListFor Enums


84

Намагаючись оновити свій проект до MVC3, чогось я просто не можу знайти:

У мене простий тип даних ENUMS:

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

Який я хочу використовувати як DropDown / SelectList на мій погляд на модель, що містить цей тип даних:

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

Досить прямо: коли я йду використовувати автогенерування подання для цього часткового класу, він ігнорує цей тип.

Мені потрібен простий список вибору, який встановлює значення переліку як вибраний елемент, коли я натискаю кнопку подання та обробки за допомогою мого методу AJAX - JSON POST.

І чим вигляд (???!):

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

заздалегідь дякую за пораду!


8
Для тих, хто стикається з цим потоком і використовує MVC 5.1 або вище, допоміжний метод @ Html.EnumDropDownListFor () тепер вбудований у MVC - див. Asp.net/mvc/overview/releases/mvc51-release-notes
mecsco

Відповіді:


55

Я щойно зробив один для власного проекту. Наведений нижче код є частиною мого допоміжного класу, я сподіваюся, що я отримав усі необхідні методи. Напишіть коментар, якщо він не працює, і я перевірю ще раз.

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

Використовуйте його як:

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

Оновлення

Я створив альтернативні помічники HTML. Все, що вам потрібно зробити, щоб використовувати їх, - це змінити свою базову сторінку перегляду в views\web.config.

З ними ви можете просто зробити:

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

Більше інформації тут: http://blog.gauffin.org/2011/10/first-draft-of-my-alternative-html-helpers/


1
Гаразд, це працює в будь-якому випадку, я просто отримую одну помилку компіляції: рядок 41: return htmlHelper.DropDownList (inputName, ToSelectList (typeof (TProperty), value.ToString ())); 'System.Web.Mvc.HtmlHelper <TModel>' не містить визначення для 'DropDownList', і не можна знайти метод розширення 'DropDownList', що приймає перший аргумент типу 'System.Web.Mvc.HtmlHelper <TModel>' ( вам не вистачає директиви використання або посилання на збірку?)
jordan.baucke

1
@jordan У мене та сама помилка. Вам вдалося вирішити проблему?
розробник SF

9
@filu @jordan додайте, using System.Web.Mvc.Html;як вам потрібно отримати доступ доSelectExtensionsClass
Саймон Хартчер

3
@Para Я отримую ту саму проблему, вибране значення не відображається вибраним у поданні. (Мені довелося помінятися ((int)item).ToString()на, Enum.GetName(enumType, item)щоб SelectListItemправильно зберегти вибране, але це все одно не працює)
Фернандо Нейра

1
Щойно додав відповідь нижче, яка охоплює питання відбору - випливає з нерозуміння поведінки перевантажень DropDownList.
Джон Егертон,

199

Тут я знайшов простіший спосіб вирішення проблеми: 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);
}   }   }

Це зробить один рядок у бритві:

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

Ви також можете знайти код для цього за допомогою методу розширення у зв’язаній статті.


6
Я думаю, що цей слід було позначити як рішення. Найкраще відзначається не складністю, а простотою.
Lord of Scripts

3
Для людей , які дивляться версію DropDowList (як я): @ Html.DropDownList ( "назва_списку", новий SelectList (Enum.GetValues (TYPEOF (MyNamespace.Enums.States))))
DStR

2
@JonEgerton Якщо ви маєте на увазі те саме, що і я, я згоден. Якщо ви хочете відобразити переліки + опис + зображення, ви втратили рішення Майка Маклафліна.
Елізабет

1
Єдина проблема, яку я знайшов із цими рішеннями, полягає в тому, що воно не відображає вибране значення правильно під час його завантаження. Окрім цього, досить добре.
triangulito

3
@triangulito це зовсім не проблема :)@Html.DropDownListFor(model => model.State, new SelectList(Enum.GetValues(typeof(MyNamespace.Enums.States)),model.State))
VladL


17

Якщо ви хочете щось дуже просте, тоді є інший спосіб, залежно від того, як ви зберігаєте стан у базі даних.

Якщо у вас була така організація:

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

Тоді генерування випадаючого меню було б настільки ж простим, як це:

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

Хіба LINQ не гарний?


де ви визначаєте перелік штатів у Моделі чи Погляді?
суперарції

в моделі, як вона використовується класом моделі
sjmeverett

1
@stewartml Коли мій ViewModel має властивість enum + "SelectedCodeProperty", це занадто багато властивостей у вашому дописі. Чому б не перерахувати обидва як вибране значення, відправити назад на сервер + як значення елемента.
Елізабет

13

Я зміг зробити це в одному лайнері.

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

8

На основі прийнятої відповіді @jgauffin я створив власну версію EnumDropDownListFor, яка стосується проблеми вибору предметів.

Проблема детально описана в іншій відповіді SO :, і в основному зводиться до нерозуміння поведінки різних перевантажень DropDownList.

Мій повний код (який включає перевантаження для htmlAttributesтощо:

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

Я написав це у своєму блозі тут .


1
Це єдине рішення, з яким я стикався, яке правильно попередньо вибирає відповідне значення для мого перерахування. Дякую!
Едвін Гронендаал,

Приголомшливо Це однозначно має бути прийнятою відповіддю - це працює; прийнята відповідь не відповідає.
neminem

3

Це було б корисно для вибору цілочисельного значення з перерахування: Ось SpecTypeце intполе ... і enmSpecTypeце enum.

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

3

Я вніс наступну зміну до методу SelectList, щоб він працював трохи краще для мене. Можливо, це буде корисно для інших.

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

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

де ви визначаєте EnumState?
суперарції

зверху ви це бачите ... загальний перелік EnumStates
Туласірам

2

Так само, як і у Майка (який занурений у довгі відповіді)

model.truckimagelocation - це властивість екземпляра класу типу перерахування TruckImageLocation

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

2

Це найбільш загальний код, який буде використовуватися для всіх Enum.

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

Метод дії

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

View.cshtml

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

1

Ви можете використовувати перелік у своїй моделі

ваш перелік

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

зробити модель

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

з огляду

@Html.Dropdownlistfor(a=>a.statesprop)

Останні запитання Відповідь kar.
Ануп

1

Найпростіша відповідь у MVC5 - це Define Enum:

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

Зв’язати в полі зору:

        <div class="form-group">
            <label>Reorder Level</label>
            @Html.EnumDropDownListFor(m => m.ReorderLevel, "Choose Me", new { @class = "form-control" })
        </div>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.