asp.net - مطاطية - كيفية عمل قائمة منسدلة مفلترة




كيف يمكنك إنشاء قائمة منسدلة من تعداد في ASP.NET MVC؟ (20)

أحاول استخدام أسلوب ملحق Html.DropDownList ولكن لا يمكن معرفة كيفية استخدامه مع التعداد.

لنفترض أن لديّ تعدادًا كالتالي:

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

كيف أقوم بإنشاء قائمة منسدلة باستخدام هذه القيم باستخدام طريقة ملحق Html.DropDownList ؟

أو أفضل رهان ببساطة لإنشاء حلقة for وإنشاء عناصر Html يدويًا؟


لاستخدام MVC v5.1 Html.EnumDropDownListFor

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

لاستخدام MVC v5 EnumHelper

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

ل MVC 5 وأقل

تدحرجت رد رون إلى طريقة تمديد:

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

هذا يسمح لك بالكتابة:

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

using MyApp.Common


أعلم أنني متأخر على الحفلة في هذا الشأن ، ولكنني أعتقد أنك قد تجد هذا البديل مفيدًا ، حيث يسمح لك هذا أيضًا باستخدام سلاسل وصفية بدلاً من ثواب التعداد في القائمة المنسدلة. للقيام بذلك ، قم بتزيين كل إدخال التعداد باستخدام سمة [System.ComponentModel.Description].

فمثلا:

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

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

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

هنا الكود:

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

يمكنك القيام بذلك في طريقة العرض الخاصة بك:

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

آمل أن يساعدك هذا!

EDIT 2014-JAN-23: قامت Microsoft للتو بإصدار MVC 5.1 ، والذي يحتوي الآن على ميزة EnumDropDownListFor. للأسف ، لا يبدو أنه يحترم السمة [Description] لذا لا يزال الرمز أعلاه قائمًا. (راجع http://www.asp.net/mvc/overview/releases/mvc51-release-notes#Enum لملاحظات إصدار Microsoft.)

تحديث: إنه يدعم السمة Display [Display(Name = "Sample")] رغم ذلك ، لذا يمكن للمرء استخدام ذلك.

[تحديث - لاحظت هذا فقط ، ويبدو الرمز نسخة ممتدة من الشفرة هنا: http://blogs.msdn.com/b/stuartleeks/archive/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-for-enums.aspx ، مع بضعة إضافات. إذا كان الأمر كذلك ، فستبدو الإحالة عادلة ؛-)]


أنا متأخرة جداً على هذا لكنني وجدت طريقة رائعة فعلاً للقيام بذلك مع سطر واحد من التعليمات البرمجية ، إذا كنت سعيداً بإضافة حزمة Unconstrained Melody NuGet (مكتبة صغيرة جميلة من جون سكيت).

هذا الحل أفضل لأن:

  1. يضمن (مع قيود النوع العامة) أن القيمة هي قيمة تعداد (بسبب ميل غير مقيد)
  2. يتجنب الملاكمة غير الضرورية (بسبب اللحن غير المقيد)
  3. انها تخبئ جميع الأوصاف لتجنب استخدام التأمل في كل مكالمة (بسبب Unconstrained Melody)
  4. إنه رمز أقل من الحلول الأخرى!

إذن ، فيما يلي خطوات تنفيذ هذا العمل:

  1. في وحدة تحكم إدارة الحزم ، "تثبيت حزمة UnconstrainedMelody"
  2. أضف خاصية على نموذجك مثل:

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

الآن بعد أن تكون لديك قائمة SelectListItem مكشوفة في الطراز الخاص بك ، يمكنك استخدام @ Html.DropDownList أو @ Html.DropDownListFor باستخدام هذه الخاصية كمصدر.


استنادًا إلى إجابة سيمون ، يتمثل أسلوب مماثل في الحصول على قيم التعداد من ملف الموارد ، بدلاً من سمة الوصف داخل التعداد نفسه. يفيد ذلك إذا كان موقعك بحاجة إلى تقديم بأكثر من لغة واحدة ، وإذا كان لديك ملف مورد محدد لـ Enums ، فيمكنك الانتقال خطوة إلى الأمام والحصول على قيم التعداد فقط في تعدادك ثم الرجوع إليها من الامتداد بواسطة اتفاقية مثل [EnumName] _ [EnumValue] - أقل كتابة في النهاية!

يبدو الامتداد كما يلي:

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

الموارد في ملف Enums.Resx تبدو مثل ItemTypes_Movie: فيلم

هناك شيء آخر أحب القيام به ، بدلاً من استدعاء طريقة الإضافة مباشرة ، فأنا أفضل تسميتها بـ @ Html.EditorFor (x => x.MyProperty) ، أو من الناحية المثالية فقط أن يكون النموذج بأكمله ، في واحد أنيق @ Html.EditorForModel (). للقيام بذلك أقوم بتغيير قالب السلسلة لتبدو مثل هذا

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

إذا كان هذا يهمك ، فقد وضعت إجابة أكثر تفصيلاً هنا على مدونتي:

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


التوسيع على إجابات الجائزة ورون ، إذا كنت ترغب في تعيين سمة القيمة لعناصر القائمة المحددة الخاصة بك إلى القيمة الصحيحة لنوع التعداد ، بدلاً من قيمة السلسلة ، استخدم الكود التالي:

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

بدلاً من التعامل مع كل قيمة تعداد ككائن TEnum ، يمكننا التعامل معها ككائن ثم إرسالها إلى عدد صحيح للحصول على القيمة غير المحفوظة.

ملاحظة: أضفت أيضًا قيدًا نوعًا عامًا لتقييد الأنواع التي يتوفر لها هذا الملحق للبنى فقط (نوع قاعدة التعداد) ، والتحقق من نوع وقت التشغيل الذي يضمن أن البنية التي تم تمريرها هي فعلاً تعداد.

تحديث 10/23/12: تمت إضافة معلمة النوع العامة للنوع الأساسي والمشكلة الثابتة غير المجمعة التي تؤثر على .NET 4+.


تريد أن تنظر في استخدام شيء مثل Enum.GetValues


صدمت في نفس المشكلة ، وجدت هذا السؤال ، وأعتقد أن الحل المقدم من الرماد لم يكن ما كنت أبحث عنه. يعني إنشاء HTML بنفسي أقل مرونة بالمقارنة مع الدالة Html.DropDownList() المضمنة.

يتحول C # 3 وما إلى ذلك يجعل هذا سهل جدا. لدي enum يسمى 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);

يؤدي هذا إلى إنشاء أمر جيد " SelectList يمكن استخدامه كما اعتدت عليه في العرض:

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

نوع مجهول و LINQ يجعل هذا IMHO أكثر من ذلك بكثير أنيقة. لا جريمة تهدف ، الرماد. :)


طريقة سهلة جدًا لتنفيذ ذلك - بدون كل العناصر الإضافية التي تبدو مبالغًا فيها هي:

التعداد الخاص بك:

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

داخل وحدة التحكم الخاصة بك ربط التعداد إلى قائمة:

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

بعد ذلك رميها في ViewBag:

    ViewBag.RequiredLevel = new SelectList(myLevels);

وأخيرًا ، ما عليك سوى ربطها بـ

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

هذا هو أسهل طريقة وجدت ولا يتطلب أي ملحقات أو أي شيء مجنون.

استكمال : انظر تعليق اندروز أدناه.


لحل مشكلة الحصول على الرقم بدلاً من النص باستخدام طريقة تمديد 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);
}

لذلك دون وظائف ملحق إذا كنت تبحث عن بسيطة وسهلة .. هذا ما فعلته

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

حيث XXXXX.Sites.YYYY.Models.State هو التعداد

ربما من الأفضل القيام بوظيفة المساعد ، ولكن عندما يكون الوقت قصيرًا ، سيؤدي ذلك إلى إنجاز المهمة.


هذه هي إجابات Rune & Prize التي تم تغييرها لاستخدام قيمة int التعدادية int .

عينة التعداد:

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

طريقة التمديد:

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

عينة من الاستخدام:

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

تذكر لاستيراد مساحة الاسم التي تحتوي على طريقة ملحق

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

عينة من HTML تم إنشاؤه:

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

لاحظ أن العنصر الذي تستخدمه لاستدعاء ToSelectList هو العنصر المحدد.


هنا حل مغلف بشكل أفضل:

http://www.spicelogic.com/Journal/ASP-NET-MVC-DropDownListFor-Html-Helper-Enum-5

قل هنا هو النموذج الخاص بك:

عينة الاستخدام:

واجهة المستخدم التي تم إنشاؤها:

وتوليد HTML

رمز لقطة التعليمات البرمجية المصدر المساعد:

يمكنك تنزيل نموذج المشروع من الرابط الذي قدمته.

تعديل: فيما يلي الكود:

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

@Simon Goldstone: Thanks for your solution, it can be perfectly applied in my case. The only problem is I had to translate it to VB. But now it is done and to save other people's time (in case they need it) I put it here:

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

End You use it like this:

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

Another fix to this extension method - the current version didn't select the enum's current value. I fixed the last line:

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

I ended up creating extention methods to do what is essentially the accept answer here. The last half of the Gist deals with Enum specifically.

https://gist.github.com/3813767


I found an answer here http://blogs.msdn.com/b/stuartleeks/archive/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-for-enums.aspx ; however, some of my enums have [Description(...)] attribute, so I've modified the code to provide support for that:

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

امل ان يساعد.


This is my version of helper method. انا استعمل هذا:

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

Instead of that:

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

ها هو:

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

You can also use my custom HtmlHelpers in Griffin.MvcContrib. The following code:

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

Generates:

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


@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" }

})

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




asp.net-mvc