Làm thế nào để bạn tạo một danh sách thả xuống từ một enum trong ASP.NET MVC?


668

Tôi đang cố gắng sử dụng Html.DropDownListphương thức mở rộng nhưng không thể tìm ra cách sử dụng nó với bảng liệt kê.

Hãy nói rằng tôi có một bảng liệt kê như thế này:

public enum ItemTypes
{
    Movie = 1,
    Game = 2,
    Book = 3
}

Làm cách nào để tạo một danh sách thả xuống với các giá trị này bằng Html.DropDownListphương thức tiện ích mở rộng?

Hoặc đặt cược tốt nhất của tôi chỉ đơn giản là tạo một vòng lặp for và tạo các phần tử Html bằng tay?

Câu trả lời:


841

Đối với MVC v5.1, hãy sử dụng Html.EnumDropDownListFor

@Html.EnumDropDownListFor(
    x => x.YourEnumField,
    "Select My Type", 
    new { @class = "form-control" })

Đối với MVC v5, hãy sử dụng EnumHelper

@Html.DropDownList("MyType", 
   EnumHelper.GetSelectList(typeof(MyType)) , 
   "Select My Type", 
   new { @class = "form-control" })

Đối với MVC 5 trở xuống

Tôi đã đưa câu trả lời của Rune vào một phương pháp mở rộng:

namespace MyApp.Common
{
    public static class MyExtensions{
        public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
            where TEnum : struct, IComparable, IFormattable, IConvertible
        {
            var values = from TEnum e in Enum.GetValues(typeof(TEnum))
                select new { Id = e, Name = e.ToString() };
            return new SelectList(values, "Id", "Name", enumObj);
        }
    }
}

Điều này cho phép bạn viết:

ViewData["taskStatus"] = task.Status.ToSelectList();

bởi using MyApp.Common


13
Tôi không thể làm cho nó hoạt động, bạn có thể vui lòng giúp đỡ. Khi tôi làm Post.PostType.ToSelectList (); nó không nhận ra phần mở rộng?
Barbaros Alp

3
Tôi cũng không thể làm việc này. Là trạng thái Tài sản Enum của bạn trên lớp nhiệm vụ? Đây không phải là một trong những giá trị được liệt kê sao?
Daryl

9
Bạn có thể hạn chế một chút với: trong đó T: struct, IConvertible Xem: stackoverflow.com/questions/79126/ợi
Richard Garside

8
Điều này thật tuyệt Nếu bất cứ ai đang vật lộn với việc thực hiện thì đây là cách tôi đã làm. Đã thêm một lớp EnumHelpers vào thư mục HtmlHelpers. Đã sử dụng đoạn mã trên. Đã thêm không gian tên cho mỗi đề xuất @TodK: <add namepace = "xxx.HtmlHelpers" />. Sau đó, tôi đã sử dụng nó trong một trang dao cạo như: @ Html.DropDownListFor (model => model.Status, @ Model.Status.ToSelectList ()) HTH
Jeff Borden

6
Lưu ý rằng trong bản mới hơn ASP.NET MVCcó một cách riêng: stackoverflow.com/a/22295360/1361084
Ofiris

359

Tôi biết tôi đến bữa tiệc muộn, nhưng nghĩ rằng bạn có thể thấy biến thể này hữu ích, vì biến thể này cũng cho phép bạn sử dụng các chuỗi mô tả thay vì các hằng số liệt kê trong trình đơn thả xuống. Để thực hiện việc này, hãy trang trí mỗi mục liệt kê bằng thuộc tính [System.ComponentModel.Description].

Ví dụ:

public enum TestEnum
{
  [Description("Full test")]
  FullTest,

  [Description("Incomplete or partial test")]
  PartialTest,

  [Description("No test performed")]
  None
}

Đây là mã của tôi:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Reflection;
using System.ComponentModel;
using System.Linq.Expressions;

 ...

 private static Type GetNonNullableModelType(ModelMetadata modelMetadata)
    {
        Type realModelType = modelMetadata.ModelType;

        Type underlyingType = Nullable.GetUnderlyingType(realModelType);
        if (underlyingType != null)
        {
            realModelType = underlyingType;
        }
        return realModelType;
    }

    private static readonly SelectListItem[] SingleEmptyItem = new[] { new SelectListItem { Text = "", Value = "" } };

    public static string GetEnumDescription<TEnum>(TEnum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());

        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

        if ((attributes != null) && (attributes.Length > 0))
            return attributes[0].Description;
        else
            return value.ToString();
    }

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

    public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
    {
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        Type enumType = GetNonNullableModelType(metadata);
        IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>();

        IEnumerable<SelectListItem> items = from value in values
            select new SelectListItem
            {
                Text = GetEnumDescription(value),
                Value = value.ToString(),
                Selected = value.Equals(metadata.Model)
            };

        // If the enum is nullable, add an 'empty' item to the collection
        if (metadata.IsNullableValueType)
            items = SingleEmptyItem.Concat(items);

        return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
    }

Sau đó, bạn có thể làm điều này trong quan điểm của bạn:

@Html.EnumDropDownListFor(model => model.MyEnumProperty)

Hy vọng điều này sẽ giúp bạn!

** EDIT 2014-JAN-23: Microsoft vừa phát hành MVC 5.1, hiện có tính năng EnumDropDownListFor. Đáng buồn thay, nó dường như không tôn trọng thuộc tính [Mô tả] vì vậy đoạn mã trên vẫn đứng. Phần Enum trong ghi chú phát hành của Microsoft cho MVC 5.1.

Cập nhật: Mặc dù vậy, nó hỗ trợ thuộc tính Display[Display(Name = "Sample")] , vì vậy người ta có thể sử dụng thuộc tính Display .

[Cập nhật - chỉ cần chú ý điều này và mã trông giống như một phiên bản mở rộng của mã ở đây: https://bloss.msdn.microsoft.com/stuartleeks/2010/05/21/asp-net-mvc-creating-a- dropdownlist-helper-for-enums / , với một vài bổ sung. Nếu vậy, ghi công có vẻ công bằng ;-)]


28
+1 Tôi thấy điều này hữu ích nhất trong tất cả các câu trả lời ở đây. Tôi đã có thể biến điều này thành một đoạn mã có thể tái sử dụng cao. Cảm ơn bạn!
Ed Charbeneau

43
Visual Studio có một lỗi lạ, nếu bạn không tham khảo System.Web.Mvc.Htmlthì nó nói rằng DropDownListForkhông thể tìm thấy, nhưng cũng không thể giải quyết được. Bạn phải tự làm using System.Web.Mvc.Html;. Chỉ vậy thôi.
Kezzer

1
Tôi có một biến thể của điều này trong một ý chính mà chúng tôi sử dụng trong tất cả các dự án của mình: gist.github.com/1287511
kamranicus

1
Giải pháp tuyệt vời, cảm ơn, sẽ còn tuyệt vời hơn nữa nếu bạn có thể lưu trữ kết quả của GetEnumDes mô tả
M. Mennan Kara

17
MVC 5.1 EnumDropDownListFor mới không sử dụng [Mô tả ("")] nhưng nó sử dụng [Display (Name = "")]! Thưởng thức :)
Supergibbs

195

Trong ASP.NET MVC 5.1 , họ đã thêm trình EnumDropDownListFor()trợ giúp, do đó không cần tiện ích mở rộng tùy chỉnh:

Mô hình :

public enum MyEnum
{
    [Display(Name = "First Value - desc..")]
    FirstValue,
    [Display(Name = "Second Value - desc...")]
    SecondValue
}

Xem :

@Html.EnumDropDownListFor(model => model.MyEnum)

Sử dụng Trình trợ giúp thẻ (ASP.NET MVC 6) :

<select asp-for="@Model.SelectedValue" asp-items="Html.GetEnumSelectList<MyEnum>()">

21
Điều này cần phải được đưa lên vị trí đầu tiên bằng cách nào đó

3
Bạn nên tạo một câu hỏi mới dành riêng cho MVC 5.1 và đặt câu hỏi này làm câu trả lời, sau đó gửi cho tôi một liên kết đến bài đăng để tôi có thể nâng cao yêu thích.
Kevin Heidt

2
Điều tôi không thích ở EnumDropDownListFor () là nó lưu vào DB giá trị int của enum, chứ không phải văn bản, vì vậy nếu bạn từng chọn thêm một mục enum mới, nó nhất thiết phải đi vào cuối danh sách , để không làm mất mối quan hệ của các giá trị int của cơ sở dữ liệu đã lưu với các vị trí ban đầu của các mục enum. Đó là một hạn chế không cần thiết nếu văn bản được lưu. Thêm vào đó, tôi thay vào đó có thể nhìn vào db và xem một văn bản, thay vì ints nơi tôi phải tìm các giá trị văn bản ở nơi khác. Nếu không, trình trợ giúp html này rất thuận tiện để sử dụng.
Giovanni

2
@Giovanni - bạn có thể chỉ định các giá trị số của riêng bạn.
Tommy

1
@Giovanni Thiết kế nghiêm ngặt nên gán giá trị cho mỗi mục nhập enum (nếu nó quan trọng), nếu không thì giá trị không quan trọng (và vì vậy việc đặt cái mới ở cuối không phải là vấn đề). Lưu các giá trị int sẽ tốt hơn khi lưu lưu trữ và tăng hiệu suất (khi thực hiện một số tìm kiếm).
Vua King

130

Tôi đã gặp phải vấn đề tương tự, tìm thấy câu hỏi này và nghĩ rằng giải pháp do Ash cung cấp không phải là điều tôi đang tìm kiếm; Phải tự tạo HTML có nghĩa là kém linh hoạt hơn so với Html.DropDownList()chức năng tích hợp sẵn.

Hóa ra C # 3, vv làm cho điều này khá dễ dàng. Tôi có một cuộc enumgọi TaskStatus:

var statuses = from TaskStatus s in Enum.GetValues(typeof(TaskStatus))
               select new { ID = s, Name = s.ToString() };
ViewData["taskStatus"] = new SelectList(statuses, "ID", "Name", task.Status);

Điều này tạo ra một ol 'tốt SelectListcó thể được sử dụng như bạn đã từng sử dụng trong chế độ xem:

<td><b>Status:</b></td><td><%=Html.DropDownList("taskStatus")%></td></tr>

Kiểu ẩn danh và LINQ làm cho IMHO thanh lịch hơn rất nhiều. Không có ý định xúc phạm, Ash. :)


câu trả lời tốt! tôi đã hy vọng ai đó sẽ sử dụng linq và Chọn danh sách :) Vui mừng tôi đã kiểm tra ở đây đầu tiên!
Pure.Krom

1
ID = s cho tôi DataTextField không phải là giá trị? Nguyên nhân có thể là gì ? Cảm ơn bạn
Barbaros Alp

1
Rune, tôi đã sử dụng cùng một phương thức này và DropDownList DOES chưa hiển thị khi nó đăng lên máy chủ, nó không lưu giá trị mà tôi đã chọn.
clockwiseq

5
@BarbarosAlp Để ID trở thành một số bạn sẽ cần chuyển enum thành int:select new { ID = (int)s, Name = s.ToString() };
Keith

Đây là câu trả lời tôi thích nhất vì tính đơn giản của nó. Thật xấu hổ vì bạn đã không nhận đủ tín dụng vì câu trả lời được chọn đã sử dụng giải pháp của bạn.
anar khalilov

63

Đây là một giải pháp đóng gói tốt hơn:

https://www.spicelogic.com/Blog/enum-dropdownlistfor-asp-net-mvc-5

Nói đây là mô hình của bạn:

nhập mô tả hình ảnh ở đây

Sử dụng mẫu:

nhập mô tả hình ảnh ở đây

Giao diện người dùng được tạo: nhập mô tả hình ảnh ở đây

Và tạo HTML

nhập mô tả hình ảnh ở đây

Ảnh chụp mã nguồn mở rộng của trình trợ giúp:

nhập mô tả hình ảnh ở đây

Bạn có thể tải về dự án mẫu từ liên kết tôi cung cấp.

EDIT: Đây là mã:

public static class EnumEditorHtmlHelper
{
    /// <summary>
    /// Creates the DropDown List (HTML Select Element) from LINQ 
    /// Expression where the expression returns an Enum type.
    /// </summary>
    /// <typeparam name="TModel">The type of the model.</typeparam>
    /// <typeparam name="TProperty">The type of the property.</typeparam>
    /// <param name="htmlHelper">The HTML helper.</param>
    /// <param name="expression">The expression.</param>
    /// <returns></returns>
    public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression) 
        where TModel : class
    {
        TProperty value = htmlHelper.ViewData.Model == null 
            ? default(TProperty) 
            : expression.Compile()(htmlHelper.ViewData.Model);
        string selected = value == null ? String.Empty : value.ToString();
        return htmlHelper.DropDownListFor(expression, createSelectList(expression.ReturnType, selected));
    }

    /// <summary>
    /// Creates the select list.
    /// </summary>
    /// <param name="enumType">Type of the enum.</param>
    /// <param name="selectedItem">The selected item.</param>
    /// <returns></returns>
    private static IEnumerable<SelectListItem> createSelectList(Type enumType, string selectedItem)
    {
        return (from object item in Enum.GetValues(enumType)
                let fi = enumType.GetField(item.ToString())
                let attribute = fi.GetCustomAttributes(typeof (DescriptionAttribute), true).FirstOrDefault()
                let title = attribute == null ? item.ToString() : ((DescriptionAttribute) attribute).Description
                select new SelectListItem
                  {
                      Value = item.ToString(), 
                      Text = title, 
                      Selected = selectedItem == item.ToString()
                  }).ToList();
    }
}

2
Chỉ là ý kiến ​​của tôi, nhưng tôi nghĩ câu trả lời này sạch sẽ hơn nhiều so với câu trả lời được chấp nhận. Tôi đặc biệt thích tùy chọn sử dụng thuộc tính Mô tả. Tôi đã thêm mã để mọi người có thể sao chép / dán mã mà không cần tải xuống.
Ben Mills

Gọi phương thức mở rộng là EnumDropDownListFor thay vì DropDownListFor Cách sử dụng: -> @ Html.EnumDropDownListFor (x => x.Gender)
sandeep Talabathula

Đối với một người nào đó đang tìm kiếm thêm một yếu tố "Vui lòng chọn" trả về htmlHelper.DropDownListFor (biểu thức, createdSelectList (biểu thức.ReturnType, đã chọn, FirstEuity), "Vui lòng chọn");
Sandeep

1
Hoạt động tốt! Tuy nhiên, trên trang Chi tiết, DisplayFor () hiển thị giá trị được chọn của enum thay vì mô tả tương ứng. Tôi cho rằng điều này gọi quá tải cho DisplayFor () cho loại enum. Bất cứ ai cũng có giải pháp cho việc này?
corix010

48

Html.DropDownListFor chỉ yêu cầu IEnumerable, do đó, một giải pháp thay thế cho giải pháp của Prise như sau. Điều này sẽ cho phép bạn chỉ cần viết:

@Html.DropDownListFor(m => m.SelectedItemType, Model.SelectedItemType.ToSelectList())

[Trong đó chọnItItType là một trường trên mô hình kiểu ItemTypes của bạn và mô hình của bạn không phải là null]

Ngoài ra, bạn không thực sự cần phải khái quát hóa phương thức mở rộng vì bạn có thể sử dụng enumValue.GetType () thay vì typeof (T).

EDIT: Giải pháp tích hợp của Simon cũng ở đây và bao gồm phương pháp mở rộng ToDes mô tả.

public static class EnumExtensions
{
    public static IEnumerable<SelectListItem> ToSelectList(this Enum enumValue)
    {
        return from Enum e in Enum.GetValues(enumValue.GetType())
               select new SelectListItem
               {
                   Selected = e.Equals(enumValue),
                   Text = e.ToDescription(),
                   Value = e.ToString()
               };
    }

    public static string ToDescription(this Enum value)
    {
        var attributes = (DescriptionAttribute[])value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : value.ToString();
    }
}

Không hoạt động với tôi ('System.NullReferenceException: Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng.') ... 'Mô hình' của tôi là null ... có lẽ có liên quan đến 'GetNonNullableModelType' mà Simon có nó bao gồm
Người học

@Cristi, bạn đúng là giải pháp này không có ý định sử dụng trong điều kiện Mô hình của bạn là null. Tôi cố gắng tránh một thiết kế như vậy nói chung và khởi tạo một mô hình "Rỗng" khi đó là trường hợp.
Zaid Masud

Chà, tôi mới dùng asp mvc, nhưng tôi có khá nhiều kinh nghiệm về .Net. Cảm ơn bạn, tôi sẽ xem xét rằng bạn đã gợi ý. Btw tiện ích mở rộng ToDes mô tả của bạn nằm ngoài phạm vi 'Enum'. Tôi đoán là tốt cho chính 'Đối tượng'. Đây là những gì tôi đã sử dụng khi tôi lấy mã của Simon và làm sạch nó thêm một chút.
Học viên

@Cristi thật khó để hiểu ý của bạn là gì khi "nằm ngoài phạm vi 'Enum" nhưng có vẻ như bạn đang nói rằng phương thức mở rộng ToDescrip không được gõ mạnh vào enT của ItemTypes? Điều này là có chủ ý và làm cho phương thức mở rộng có thể sử dụng rộng rãi bởi tất cả các enum. Nếu bạn đang so sánh nó với một phương pháp mở rộng chung, có những ưu và nhược điểm của từng phương pháp. Cụ thể, nếu bạn tạo ra bạn, bạn không thể làm cho nó bị hạn chế trên enums một mình.
Zaid Masud

1
Tuyệt vời, với lời cảm ơn. Tôi đã thay đổi value.ToString để sử dụng tiện ích mở rộng FromCamelCase trong trường hợp không có mô tả. Đó là cách tôi lăn :)
Valamas

33

Vì vậy, không có chức năng mở rộng nếu bạn đang tìm kiếm đơn giản và dễ dàng .. Đây là những gì tôi đã làm

<%= Html.DropDownListFor(x => x.CurrentAddress.State, new SelectList(Enum.GetValues(typeof(XXXXX.Sites.YYYY.Models.State))))%>

trong đó XXXXX.Sites.YYYY.Models.State là một enum

Có lẽ tốt hơn để làm chức năng trợ giúp, nhưng khi thời gian ngắn, điều này sẽ hoàn thành công việc.


Rất vui vì điều này đã làm việc với việc thả xuống nhưng làm thế nào để bạn đặt giá trị được chọn mặc định trong cú pháp Dao cạo cho Html.DropDownListFor? Tôi muốn hiển thị một bảng với các hộp kết hợp của enum và tôi cần đặt giá trị đã chọn theo giá trị trước đó.
Johncl

2
Nên có thể truyền tham số thứ hai với giá trị được chọn vào hàm SelectList (IEnumerable, object) mới. Tài liệu MSDN: msdn.microsoft.com/en-us/l
Marty Trenouth

23

Mở rộng các câu trả lời của Prize và Rune, nếu bạn muốn có thuộc tính giá trị của danh sách các mục danh sách đã chọn của bạn thành giá trị số nguyên của loại liệt kê, thay vì giá trị chuỗi, hãy sử dụng mã sau:

public static SelectList ToSelectList<T, TU>(T enumObj) 
    where T : struct
    where TU : struct
{
    if(!typeof(T).IsEnum) throw new ArgumentException("Enum is required.", "enumObj");

    var values = from T e in Enum.GetValues(typeof(T))
                 select new { 
                    Value = (TU)Convert.ChangeType(e, typeof(TU)),
                    Text = e.ToString() 
                 };

    return new SelectList(values, "Value", "Text", enumObj);
}

Thay vì coi mỗi giá trị liệt kê là một đối tượng TEnum, chúng ta có thể coi nó như một đối tượng và sau đó chuyển nó thành số nguyên để lấy giá trị không được hộp.

Lưu ý: Tôi cũng đã thêm một ràng buộc loại chung để hạn chế các loại mà tiện ích mở rộng này chỉ có sẵn cho các cấu trúc (loại cơ sở của Enum) và xác thực loại thời gian chạy để đảm bảo rằng cấu trúc được truyền vào thực sự là Enum.

Cập nhật 23/11/12: Đã thêm tham số loại chung cho loại cơ bản và sự cố không biên dịch cố định ảnh hưởng đến .NET 4+.


Cảm ơn! Đây là câu trả lời tôi cần. Tôi đang lưu trữ giá trị nguyên của Enum dưới dạng cột trong cơ sở dữ liệu và giải pháp này dường như đang hoạt động hoàn hảo.
grimus

Nhưng nếu bạn đang lưu trữ một char và không phải là int thì sao? đó là trường hợp của tôi rõ ràng tôi có thể thay đổi (int) thành (char) nhưng làm thế nào để tạo ra cái chung này là tốt. làm thế nào để làm điều đó?
Stefanvds

@Stefandvds Đây là một câu hỏi hay liên quan đến việc chuyển sang loại được trình bày chính xác. Dựa trên các thử nghiệm tôi vừa thực hiện, có vẻ như cách duy nhất bạn có thể đạt được điều này là bằng cách chỉ định loại thực tế là một tham số loại khác. ToSelectList<TEnum, TEnumValue>(this TEnum enumObj) { ... }
Nathan Taylor


Nếu giá trị enum của bạn là int, bạn chỉ cần sử dụng Value = Convert.ToInt32(e). (int)ekhông biên dịch. :(
Andrew

11

Để giải quyết vấn đề lấy số thay vì văn bản bằng phương pháp tiện ích mở rộng của Prise.

public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
{
  var values = from TEnum e in Enum.GetValues(typeof(TEnum))
               select new { ID = (int)Enum.Parse(typeof(TEnum),e.ToString())
                         , Name = e.ToString() };

  return new SelectList(values, "Id", "Name", enumObj);
}

Đó là những gì tôi đang tìm kiếm, mặc dù nó xấu hơn tôi nghĩ nó cần phải có. Tôi tự hỏi tại sao Visual Studio sẽ không cho phép bạn trực tiếp đúc eđể int.
Andrew

Hoặc bạn chỉ có thể sử dụng ID = Convert.ToInt32(e).
Andrew

11

Một cách siêu dễ dàng để thực hiện việc này - không có tất cả các tiện ích mở rộng có vẻ quá mức là đây:

Enum của bạn:

    public enum SelectedLevel
    {
       Level1,
       Level2,
       Level3,
       Level4
    }

Bên trong bộ điều khiển của bạn liên kết Enum với một Danh sách:

    List<SelectedLevel> myLevels = Enum.GetValues(typeof(SelectedLevel)).Cast<SelectedLevel>().ToList();

Sau đó, ném nó vào ViewBag:

    ViewBag.RequiredLevel = new SelectList(myLevels);

Cuối cùng, chỉ cần liên kết nó với View:

    @Html.DropDownList("selectedLevel", (SelectList)ViewBag.RequiredLevel, new { @class = "form-control" })

Đây là cách dễ nhất tôi tìm thấy và không yêu cầu bất kỳ tiện ích mở rộng hoặc bất cứ điều gì điên rồ.

CẬP NHẬT : Xem bình luận của Andrew dưới đây.


3
Điều này chỉ hoạt động nếu bạn chưa gán bất kỳ giá trị nào cho enum của bạn. Nếu bạn đã có Level1 = 1, thì giá trị của danh sách thả xuống sẽ "Level1"thay thế 1.
Andrew

11

Giải pháp tốt nhất tôi tìm thấy cho việc này là kết hợp blog này với câu trả lời của Simon Goldstone .

Điều này cho phép sử dụng enum trong mô hình. Về cơ bản, ý tưởng là sử dụng một thuộc tính số nguyên cũng như enum và mô phỏng thuộc tính số nguyên.

Sau đó, sử dụng thuộc tính [System.ComponentModel.Des mô tả] để chú thích mô hình với văn bản hiển thị của bạn và sử dụng tiện ích mở rộng "EnumDropDownListFor" trong chế độ xem của bạn.

Điều này làm cho cả khung nhìn và mô hình rất dễ đọc và duy trì.

Mô hình:

public enum YesPartialNoEnum
{
    [Description("Yes")]
    Yes,
    [Description("Still undecided")]
    Partial,
    [Description("No")]
    No
}

//........

[Display(Name = "The label for my dropdown list")]
public virtual Nullable<YesPartialNoEnum> CuriousQuestion{ get; set; }
public virtual Nullable<int> CuriousQuestionId
{
    get { return (Nullable<int>)CuriousQuestion; }
    set { CuriousQuestion = (Nullable<YesPartialNoEnum>)value; }
}

Lượt xem:

@using MyProject.Extensions
{
//...
    @Html.EnumDropDownListFor(model => model.CuriousQuestion)
//...
}

Gia hạn (trực tiếp từ câu trả lời của Simon Goldstone , bao gồm ở đây để hoàn thiện):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.ComponentModel;
using System.Reflection;
using System.Linq.Expressions;
using System.Web.Mvc.Html;

namespace MyProject.Extensions
{
    //Extension methods must be defined in a static class
    public static class MvcExtensions
    {
        private static Type GetNonNullableModelType(ModelMetadata modelMetadata)
        {
            Type realModelType = modelMetadata.ModelType;

            Type underlyingType = Nullable.GetUnderlyingType(realModelType);
            if (underlyingType != null)
            {
                realModelType = underlyingType;
            }
            return realModelType;
        }

        private static readonly SelectListItem[] SingleEmptyItem = new[] { new SelectListItem { Text = "", Value = "" } };

        public static string GetEnumDescription<TEnum>(TEnum value)
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());

            DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if ((attributes != null) && (attributes.Length > 0))
                return attributes[0].Description;
            else
                return value.ToString();
        }

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

        public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            Type enumType = GetNonNullableModelType(metadata);
            IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>();

            IEnumerable<SelectListItem> items = from value in values
                                                select new SelectListItem
                                                {
                                                    Text = GetEnumDescription(value),
                                                    Value = value.ToString(),
                                                    Selected = value.Equals(metadata.Model)
                                                };

            // If the enum is nullable, add an 'empty' item to the collection
            if (metadata.IsNullableValueType)
                items = SingleEmptyItem.Concat(items);

            return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
        }
    }
}

Điều này không hoạt động, MVC 4 Dao cạo. Trong chế độ xem hoặc thời gian chạy, error = "Cuộc gọi không rõ ràng giữa các phương thức hoặc thuộc tính sau 'LDN.Extensions.MvcExtensions.EnumDropDownListFor <MyModel, LDN.Models.YesPartialNoEnum?> (System.Web.Mvc.Hel .Linq.Expressions.Expression <System.Func <MyModel, LDN.Models.YesPartialNoEnum? >>) '....
Marc


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

Tốt Làm thế nào để có được giá trị và văn bản từ enum theo cách này? Ý tôi là tôi có someEnum {some1 = 1, some2 = 2} Tôi cần lấy số (1, 2) cho giá trị và văn bản (some1, some2) cho văn bản của
Dmitresky

7

Đây là câu trả lời của Rune & Prize được thay đổi để sử dụng intgiá trị Enum làm ID.

Mẫu Enum:

public enum ItemTypes
{
    Movie = 1,
    Game = 2,
    Book = 3
}

Phương pháp mở rộng:

    public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
    {
        var values = from TEnum e in Enum.GetValues(typeof(TEnum))
                     select new { Id = (int)Enum.Parse(typeof(TEnum), e.ToString()), Name = e.ToString() };

        return new SelectList(values, "Id", "Name", (int)Enum.Parse(typeof(TEnum), enumObj.ToString()));
    }

Mẫu sử dụng:

 <%=  Html.DropDownList("MyEnumList", ItemTypes.Game.ToSelectList()) %>

Nhớ nhập không gian tên chứa phương thức Tiện ích mở rộng

<%@ Import Namespace="MyNamespace.LocationOfExtensionMethod" %>

Mẫu HTML được tạo:

<select id="MyEnumList" name="MyEnumList">
    <option value="1">Movie</option>
    <option selected="selected" value="2">Game</option>
    <option value="3">Book </option>
</select>

Lưu ý rằng mục mà bạn sử dụng để gọi ToSelectListlà mục đã chọn.


Hoặc bạn chỉ có thể sử dụng Id = Convert.ToInt32(e).
Andrew

6

Đây là phiên bản dành cho Dao cạo:

@{
    var itemTypesList = new List<SelectListItem>();
    itemTypesList.AddRange(Enum.GetValues(typeof(ItemTypes)).Cast<ItemTypes>().Select(
                (item, index) => new SelectListItem
                {
                    Text = item.ToString(),
                    Value = (index).ToString(),
                    Selected = Model.ItemTypeId == index
                }).ToList());
 }


@Html.DropDownList("ItemTypeId", itemTypesList)

Điều đó sẽ chỉ hoạt động nếu enum của bạn bao gồm các giá trị liền kề bắt đầu bằng 0. Một enum cờ sẽ không hoạt động với điều này. Mặc dù vậy, việc sử dụng sáng tạo của Chọn được lập chỉ mục.
Suncat2000

6

Trong .NET Core, bạn chỉ có thể sử dụng điều này:

@Html.DropDownListFor(x => x.Foo, Html.GetEnumSelectList<MyEnum>())

1
Hoặc với trình trợ giúp thẻ <select asp-for="Model.Foo" class="form-control" asp-items="@Html.GetEnumSelectList<MyEnum>()"></select>.
Pascal R.

vâng, Id nói những người trợ giúp thẻ thậm chí còn tốt hơn vì định dạng gần với HTML thuần túy hơn;)
GoldenAge

Ngoài ra, bạn có thể làm điều này @ Html.DropDownListFor (x => x.Foo, Html.GetEnumSelectList (typeof (FooEnum)))
Fereydoon Barikzehy


5

Dựa trên câu trả lời của Simon, một cách tiếp cận tương tự là lấy các giá trị Enum hiển thị từ tệp Tài nguyên, thay vì trong thuộc tính mô tả trong chính Enum. Điều này hữu ích nếu trang web của bạn cần được hiển thị bằng nhiều ngôn ngữ và nếu bạn có tệp tài nguyên cụ thể cho Enums, bạn có thể tiến thêm một bước và chỉ có các giá trị Enum, trong Enum của bạn và tham chiếu chúng từ tiện ích mở rộng một quy ước như [EnumName] _ [EnumValue] - cuối cùng ít gõ hơn!

Phần mở rộng sau đó trông như:

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 = from enumValue in enumValues                        
                select new SelectListItem
                {
                    Text = GetResourceValueForEnumValue(enumValue),
                    Value = ((int)enumValue).ToString(),
                    Selected = enumValue.Equals(metadata.Model)
                };


    return html.DropDownListFor(expression, items, string.Empty, null);
}

private static string GetResourceValueForEnumValue<TEnum>(TEnum enumValue)
{
    var key = string.Format("{0}_{1}", enumValue.GetType().Name, enumValue);

    return Enums.ResourceManager.GetString(key) ?? enumValue.ToString();
}

Tài nguyên trong tệp Enums.Resx trông giống như ItemTypes_Movie: Film

Một điều khác tôi muốn làm là, thay vì gọi trực tiếp phương thức tiện ích mở rộng, tôi muốn gọi nó bằng @ Html.EditorFor (x => x.MyProperty) hoặc lý tưởng là chỉ có toàn bộ biểu mẫu, trong một gọn gàng @ Html.EditorForModel (). Để làm điều này, tôi thay đổi mẫu chuỗi thành như thế này

@using MVCProject.Extensions

@{
    var type = Nullable.GetUnderlyingType(ViewData.ModelMetadata.ModelType) ?? ViewData.ModelMetadata.ModelType;

    @(typeof (Enum).IsAssignableFrom(type) ? Html.EnumDropDownListFor(x => x) : Html.TextBoxFor(x => x))
}

Nếu điều này làm bạn quan tâm, tôi đã đặt câu trả lời chi tiết hơn nhiều ở đây trên blog của mình:

http://paulthecyclist.com/2013/05/24/enum-dropdown/


5

Chà, tôi thực sự đến bữa tiệc muộn, nhưng với những gì đáng giá, tôi đã viết blog về chính chủ đề này, qua đó tôi tạo ra một EnumHelperlớp học cho phép chuyển đổi rất dễ dàng.

http://jnye.co/Posts/4/creating-a-dropdown-list-from-an-enum-in-mvc-and-c%23

Trong bộ điều khiển của bạn:

//If you don't have an enum value use the type
ViewBag.DropDownList = EnumHelper.SelectListFor<MyEnum>();

//If you do have an enum value use the value (the value will be marked as selected)    
ViewBag.DropDownList = EnumHelper.SelectListFor(MyEnum.MyEnumValue);

Trong chế độ xem của bạn:

@Html.DropDownList("DropDownList")
@* OR *@
@Html.DropDownListFor(m => m.Property, ViewBag.DropDownList as SelectList, null)

Lớp người trợ giúp:

public static class EnumHelper
{
    // Get the value of the description attribute if the   
    // enum has one, otherwise use the value.  
    public static string GetDescription<TEnum>(this TEnum value)
    {
        var fi = value.GetType().GetField(value.ToString());

        if (fi != null)
        {
            var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attributes.Length > 0)
            {
                return attributes[0].Description;
            }
        }

        return value.ToString();
    }

    /// <summary>
    /// Build a select list for an enum
    /// </summary>
    public static SelectList SelectListFor<T>() where T : struct
    {
        Type t = typeof(T);
        return !t.IsEnum ? null
                         : new SelectList(BuildSelectListItems(t), "Value", "Text");
    }

    /// <summary>
    /// Build a select list for an enum with a particular value selected 
    /// </summary>
    public static SelectList SelectListFor<T>(T selected) where T : struct
    {
        Type t = typeof(T);
        return !t.IsEnum ? null
                         : new SelectList(BuildSelectListItems(t), "Text", "Value", selected.ToString());
    }

    private static IEnumerable<SelectListItem> BuildSelectListItems(Type t)
    {
        return Enum.GetValues(t)
                   .Cast<Enum>()
                   .Select(e => new SelectListItem { Value = e.ToString(), Text = e.GetDescription() });
    }
}

4

Tôi rất muộn về điều này nhưng tôi chỉ tìm thấy một cách thực sự thú vị để làm điều này với một dòng mã, nếu bạn vui lòng thêm gói Unconstrained Melody NuGet (một thư viện nhỏ, đẹp từ Jon Skeet).

Giải pháp này tốt hơn vì:

  1. Nó đảm bảo (với các ràng buộc kiểu chung) rằng giá trị thực sự là giá trị enum (do Giai điệu không giới hạn)
  2. Nó tránh được quyền anh không cần thiết (do Giai điệu không bị ràng buộc)
  3. Nó lưu trữ tất cả các mô tả để tránh sử dụng phản xạ trên mỗi cuộc gọi (do Giai điệu không giới hạn)
  4. Nó là ít mã hơn các giải pháp khác!

Vì vậy, đây là các bước để làm việc này:

  1. Trong Bảng điều khiển quản lý gói, "Cài đặt gói UnconstrainedMelody"
  2. Thêm một thuộc tính trên mô hình của bạn như vậy:

    //Replace "YourEnum" with the type of your enum
    public IEnumerable<SelectListItem> AllItems
    {
        get
        {
            return Enums.GetValues<YourEnum>().Select(enumValue => new SelectListItem { Value = enumValue.ToString(), Text = enumValue.GetDescription() });
        }
    }

Bây giờ bạn đã hiển thị Danh sách Chọn ListItem trên mô hình của mình, bạn có thể sử dụng @ Html.DropDownList hoặc @ Html.DropDownListFor bằng cách sử dụng thuộc tính này làm nguồn.


+1 để sử dụng mã của Jon Skeet :), dù sao cũng chỉ đùa thôi
Vamsi

3

Một cách khắc phục khác cho phương thức tiện ích mở rộng này - phiên bản hiện tại không chọn giá trị hiện tại của enum. Tôi đã sửa dòng cuối cùng:

public static SelectList ToSelectList<TEnum>(this TEnum enumObj) where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum) throw new ArgumentException("An Enumeration type is required.", "enumObj");

        var values = from TEnum e in Enum.GetValues(typeof(TEnum))
                       select new
                       {
                           ID = (int)Enum.Parse(typeof(TEnum), e.ToString()),
                           Name = e.ToString()
                       };


        return new SelectList(values, "ID", "Name", ((int)Enum.Parse(typeof(TEnum), enumObj.ToString())).ToString());
    }

3

Nếu bạn muốn thêm hỗ trợ bản địa hóa, chỉ cần thay đổi phương thức s.toString () thành một cái gì đó như thế này:

ResourceManager rManager = new ResourceManager(typeof(Resources));
var dayTypes = from OperatorCalendarDay.OperatorDayType s in Enum.GetValues(typeof(OperatorCalendarDay.OperatorDayType))
               select new { ID = s, Name = rManager.GetString(s.ToString()) };

Ở đây, typeof (Tài nguyên) là tài nguyên bạn muốn tải và sau đó bạn nhận được Chuỗi được bản địa hóa, cũng hữu ích nếu trình liệt kê của bạn có các giá trị có nhiều từ.


3

Đây là phiên bản của tôi về phương pháp trợ giúp. Tôi sử dụng cái này:

var values = from int e in Enum.GetValues(typeof(TEnum))
             select new { ID = e, Name = Enum.GetName(typeof(TEnum), e) };

Thay vì đó:

var values = from TEnum e in Enum.GetValues(typeof(TEnum))
           select new { ID = (int)Enum.Parse(typeof(TEnum),e.ToString())
                     , Name = e.ToString() };

Đây là:

public static SelectList ToSelectList<TEnum>(this TEnum self) where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum)
        {
            throw new ArgumentException("self must be enum", "self");
        }

        Type t = typeof(TEnum);

        var values = from int e in Enum.GetValues(typeof(TEnum))
                     select new { ID = e, Name = Enum.GetName(typeof(TEnum), e) };

        return new SelectList(values, "ID", "Name", self);
    }

3

Bạn cũng có thể sử dụng HtmlHelpers tùy chỉnh của tôi trong Griffin.MvcContrib. Các mã sau đây:

@Html2.CheckBoxesFor(model => model.InputType) <br />
@Html2.RadioButtonsFor(model => model.InputType) <br />
@Html2.DropdownFor(model => model.InputType) <br />

Tạo:

nhập mô tả hình ảnh ở đây

https://github.com/jgauffin/griffin.mvccontrib


3

Tôi muốn trả lời câu hỏi này theo một cách khác, trong đó, người dùng không cần phải làm bất cứ điều gì trong controllerhoặc Linqbiểu hiện. Cách này...

tôi có một ENUM

public enum AccessLevelEnum
    {
        /// <summary>
        /// The user cannot access
        /// </summary>
        [EnumMember, Description("No Access")]
        NoAccess = 0x0,

        /// <summary>
        /// The user can read the entire record in question
        /// </summary>
        [EnumMember, Description("Read Only")]
        ReadOnly = 0x01,

        /// <summary>
        /// The user can read or write
        /// </summary>
        [EnumMember, Description("Read / Modify")]
        ReadModify = 0x02,

        /// <summary>
        /// User can create new records, modify and read existing ones
        /// </summary>
        [EnumMember, Description("Create / Read / Modify")]
        CreateReadModify = 0x04,

        /// <summary>
        /// User can read, write, or delete
        /// </summary>
        [EnumMember, Description("Create / Read / Modify / Delete")]
        CreateReadModifyDelete = 0x08,

        /*/// <summary>
        /// User can read, write, or delete
        /// </summary>
        [EnumMember, Description("Create / Read / Modify / Delete / Verify / Edit Capture Value")]
        CreateReadModifyDeleteVerify = 0x16*/
    }

Bây giờ tôi chỉ đơn giản là tạo ra một dropdownbằng cách sử dụng này enum.

@Html.DropDownList("accessLevel",new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum))),new { @class = "form-control" })

HOẶC LÀ

@Html.DropDownListFor(m=>m.accessLevel,new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum))),new { @class = "form-control" })

Nếu bạn muốn tạo một chỉ mục được chọn thì hãy thử điều này

@Html.DropDownListFor(m=>m.accessLevel,new SelectList(AccessLevelEnum.GetValues(typeof(AccessLevelEnum)) , AccessLevelEnum.NoAccess ),new { @class = "form-control" })

Ở đây tôi đã sử dụng AccessLevelEnum.NoAccessnhư một tham số bổ sung cho mặc định chọn thả xuống.


3

Tôi tìm thấy một câu trả lời ở đây . Tuy nhiên, một số enum của tôi có [Description(...)]thuộc tính, vì vậy tôi đã sửa đổi mã để cung cấp hỗ trợ cho điều đó:

    enum Abc
    {
        [Description("Cba")]
        Abc,

        Def
    }


    public static MvcHtmlString EnumDropDownList<TEnum>(this HtmlHelper htmlHelper, string name, TEnum selectedValue)
    {
        IEnumerable<TEnum> values = Enum.GetValues(typeof(TEnum))
            .Cast<TEnum>();

        List<SelectListItem> items = new List<SelectListItem>();
        foreach (var value in values)
        {
            string text = value.ToString();

            var member = typeof(TEnum).GetMember(value.ToString());
            if (member.Count() > 0)
            {
                var customAttributes = member[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
                if (customAttributes.Count() > 0)
                {
                    text = ((DescriptionAttribute)customAttributes[0]).Description;
                }
            }

            items.Add(new SelectListItem
            {
                Text = text,
                Value = value.ToString(),
                Selected = (value.Equals(selectedValue))
            });
        }

        return htmlHelper.DropDownList(
            name,
            items
            );
    }

Mong rằng sẽ giúp.


Tôi muốn trả về một thành viên của loại = DropdownList. Tôi tốt với Văn bản = Mô tả
Thuộc tính

2

@Simon Goldstone: Cảm ơn giải pháp của bạn, nó có thể được áp dụng hoàn hảo trong trường hợp của tôi. Vấn đề duy nhất là tôi phải dịch nó sang VB. Nhưng bây giờ nó đã được thực hiện và để tiết kiệm thời gian của người khác (trong trường hợp họ cần), tôi đặt nó ở đây:

Imports System.Runtime.CompilerServices
Imports System.ComponentModel
Imports System.Linq.Expressions

Public Module HtmlHelpers
    Private Function GetNonNullableModelType(modelMetadata As ModelMetadata) As Type
        Dim realModelType = modelMetadata.ModelType

        Dim underlyingType = Nullable.GetUnderlyingType(realModelType)

        If Not underlyingType Is Nothing Then
            realModelType = underlyingType
        End If

        Return realModelType
    End Function

    Private ReadOnly SingleEmptyItem() As SelectListItem = {New SelectListItem() With {.Text = "", .Value = ""}}

    Private Function GetEnumDescription(Of TEnum)(value As TEnum) As String
        Dim fi = value.GetType().GetField(value.ToString())

        Dim attributes = DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())

        If Not attributes Is Nothing AndAlso attributes.Length > 0 Then
            Return attributes(0).Description
        Else
            Return value.ToString()
        End If
    End Function

    <Extension()>
    Public Function EnumDropDownListFor(Of TModel, TEnum)(ByVal htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TEnum))) As MvcHtmlString
        Return EnumDropDownListFor(htmlHelper, expression, Nothing)
    End Function

    <Extension()>
    Public Function EnumDropDownListFor(Of TModel, TEnum)(ByVal htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TEnum)), htmlAttributes As Object) As MvcHtmlString
        Dim metaData As ModelMetadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData)
        Dim enumType As Type = GetNonNullableModelType(metaData)
        Dim values As IEnumerable(Of TEnum) = [Enum].GetValues(enumType).Cast(Of TEnum)()

        Dim items As IEnumerable(Of SelectListItem) = From value In values
            Select New SelectListItem With
            {
                .Text = GetEnumDescription(value),
                .Value = value.ToString(),
                .Selected = value.Equals(metaData.Model)
            }

        ' If the enum is nullable, add an 'empty' item to the collection
        If metaData.IsNullableValueType Then
            items = SingleEmptyItem.Concat(items)
        End If

        Return htmlHelper.DropDownListFor(expression, items, htmlAttributes)
    End Function
End Module

Kết thúc Bạn sử dụng nó như thế này:

@Html.EnumDropDownListFor(Function(model) (model.EnumField))


2
@Html.DropdownListFor(model=model->Gender,new List<SelectListItem>
{
 new ListItem{Text="Male",Value="Male"},
 new ListItem{Text="Female",Value="Female"},
 new ListItem{Text="--- Select -----",Value="-----Select ----"}
}
)

2
@Html.DropDownListFor(model => model.MaritalStatus, new List<SelectListItem> 
{  

new SelectListItem { Text = "----Select----", Value = "-1" },


new SelectListItem { Text = "Marrid", Value = "M" },


 new SelectListItem { Text = "Single", Value = "S" }

})

Tôi nghĩ rằng đây không phải là một câu trả lời hợp lệ, nó hoàn toàn không sử dụng enum để đưa vào danh sách thả xuống.
Andrew
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.