Avatar ·

Как создать выпадающий список из перечисления enum в ASP.NET MVC?

Привет, коллеги! Я начинающий разработчик на C# и ASP.NET MVC, и у меня возникла проблема, которая кажется простой, но я никак не могу найти элегантное решение. У меня есть перечисление (enum) для статусов заказа: ```csharp public enum OrderStatus { New = 0, Processing = 1, Shipped = 2, Delivered = 3, Cancelled = 4 } ``` И у меня есть модель представления (ViewModel), которая содержит это свойство: ```csharp public class OrderViewModel { public int OrderId { get; set; } public OrderStatus SelectedStatus { get; set; } // другие свойства... } ``` В представлении (View) я хочу создать выпадающий список (DropdownList), который будет содержать все значения из перечисления OrderStatus. Пользователь должен выбрать один из статусов, и при отправке формы выбранное значение должно привязываться к свойству SelectedStatus. Проблема в том, что я не знаю, как правильно преобразовать enum в список SelectListItem для использования в Html.DropDownListFor. Я пробовал писать вручную список, но это неудобно и не масштабируется. Также я хочу, чтобы отображались читаемые имена (например, "В обработке" вместо "Processing"), а не просто названия констант. Подскажите, пожалуйста, как это правильно сделать? Может быть, есть какой-то стандартный подход или хелпер? Буду благодарен за пример кода. <<>>

Решение: создание универсального хелпера для enum

Отличный вопрос! В ASP.NET MVC действительно нет встроенного способа напрямую преобразовать enum в выпадающий список, но это легко решается с помощью пары вспомогательных методов. Вот самый чистый и переиспользуемый подход, который я использую в своих проектах.

Шаг 1: Создаем хелпер-расширение для Enum

Сначала напишем статический класс с методами расширения, которые будут преобразовывать любой enum в список SelectListItem:

public static class EnumHelper
{
    /// <summary>
    /// Преобразует enum в список SelectListItem для DropDownList
    /// </summary>
    public static List<SelectListItem> ToSelectList<TEnum>(this TEnum enumObj)
        where TEnum : struct, Enum
    {
        return Enum.GetValues(typeof(TEnum))
            .Cast<TEnum>()
            .Select(value => new SelectListItem
            {
                Text = GetDisplayName(value),
                Value = Convert.ToInt32(value).ToString()
            })
            .ToList();
    }

    /// <summary>
    /// Получает отображаемое имя из атрибута Display или ToString()
    /// </summary>
    private static string GetDisplayName<TEnum>(TEnum value) where TEnum : struct, Enum
    {
        var fieldInfo = value.GetType().GetField(value.ToString());
        var displayAttribute = fieldInfo?.GetCustomAttribute<DisplayAttribute>();
        
        if (displayAttribute != null)
            return displayAttribute.GetName();
        
        return value.ToString();
    }
}

Шаг 2: Добавляем атрибуты Display к enum

Теперь украсим наш enum атрибутами [Display] для читаемых имен:

using System.ComponentModel.DataAnnotations;

public enum OrderStatus
{
    [Display(Name = "Новый")]
    New = 0,
    
    [Display(Name = "В обработке")]
    Processing = 1,
    
    [Display(Name = "Отгружен")]
    Shipped = 2,
    
    [Display(Name = "Доставлен")]
    Delivered = 3,
    
    [Display(Name = "Отменён")]
    Cancelled = 4
}

Шаг 3: Используем в контроллере

В методе действия контроллера передаём список во ViewBag или ViewModel:

public ActionResult Edit(int id)
{
    var viewModel = new OrderViewModel
    {
        OrderId = id,
        SelectedStatus = OrderStatus.New,
        // Заполняем список статусов для выпадающего списка
        StatusList = new OrderStatus().ToSelectList()
    };
    
    return View(viewModel);
}

Добавьте в ViewModel свойство для списка:

public class OrderViewModel
{
    public int OrderId { get; set; }
    public OrderStatus SelectedStatus { get; set; }
    public List<SelectListItem> StatusList { get; set; }
}

Шаг 4: В представлении Razor

И наконец, используем стандартный хелпер:

@model YourNamespace.OrderViewModel

@Html.DropDownListFor(
    m => m.SelectedStatus,
    Model.StatusList,
    "-- Выберите статус --",
    new { @class = "form-control" }
)

Преимущества этого подхода:

  • Переиспользуемость: метод ToSelectList() работает с любым enum в вашем проекте
  • Локализация: легко добавить поддержку ресурсов через атрибут Display
  • Типобезопасность: компилятор проверяет, что вы передаёте именно enum
  • Чистота кода: вся логика преобразования вынесена в отдельный класс

Если вам нужно, чтобы значение было строкой (например, "New") вместо числа, просто измените строку Value = Convert.ToInt32(value).ToString() на Value = value.ToString(). Но обычно числовые значения предпочтительнее для привязки данных.

<<>>

Альтернативный подход: использование ViewBag и ручного создания списка

Если вы не хотите создавать отдельный хелпер или ваш проект небольшой, можно сделать проще — напрямую в контроллере сформировать список SelectListItem:

public ActionResult Create()
{
    var statusList = Enum.GetValues(typeof(OrderStatus))
        .Cast<OrderStatus>()
        .Select(s => new SelectListItem
        {
            Text = s.ToString(),  // Или используйте switch для читаемых имен
            Value = ((int)s).ToString()
        })
        .ToList();

    ViewBag.StatusList = statusList;
    return View();
}

А в представлении:

@Html.DropDownList("SelectedStatus", ViewBag.StatusList as List<SelectListItem>, "Выберите статус", new { @class = "form-control" })

Недостатки:

  • Код дублируется в каждом контроллере
  • Нет локализации — отображаются технические имена (New, Processing и т.д.)
  • ViewBag не типизирован — возможны ошибки во время выполнения

Этот метод подходит для быстрых прототипов или когда enum используется только в одном месте, но для серьёзных проектов рекомендую первый вариант с хелпером.