2009-03-03 2 views
62

Продолжая из этого вопроса programmatically creating a drop down list Я бы хотел, чтобы мой список имел несколько списков optgroup. Возможно ли это в настоящее время?Поддержка optgroup в выпадающем списке .NET MVC?

Я знаю, что мне нужно передать selectList в dropDownList, но не знаю, как добавить текст, значение, optgroup в selectList.

Я хочу, чтобы конечный результат для производства:

<option value="">Please select</option> 
    <optgroup label="Option A"> 
    <option value="1">1</option> 
    <option value="2">2</option> 
    <option value="3">3</option> 
    <option value="4">4</option> 
    </optgroup> 
    <optgroup label="Option B"> 
    <option value="a">A</option> 
    <option value="b">B</option> 
    <option value="c">C</option> 
    </optgroup> 
</option> 
+3

Теперь встроенный в ASP.Net MVC версии 5.2 и далее - см. Мой ответ ниже –

ответ

13

Просматривая код на www.codeplex.com/aspnet, не кажется, что ни SelectList, ни метод расширения DropDownList поддерживает использование OPTGROUP в избранных. Похоже, вам нужно написать свой собственный метод расширения и расширить SelectListItem, чтобы содержать группировку или генерировать выбор вручную в разметке.

25

Я просто написать расширения для этого увидеть:


using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Web.Routing; 

namespace System.Web.Mvc.Html 
{ 
    public static class GroupDropListExtensions 
    { 
     public static string GroupDropList(this HtmlHelper helper, string name, IEnumerable<GroupDropListItem> data, string SelectedValue, object htmlAttributes) 
     { 
      if (data == null && helper.ViewData != null) 
       data = helper.ViewData.Eval(name) as IEnumerable<GroupDropListItem>; 
      if (data == null) return string.Empty; 

      var select = new TagBuilder("select"); 

      if (htmlAttributes != null) 
       select.MergeAttributes(new RouteValueDictionary(htmlAttributes)); 

      select.GenerateId(name); 

      var optgroupHtml = new StringBuilder(); 
      var groups = data.ToList(); 
      foreach (var group in data) 
      { 
       var groupTag = new TagBuilder("optgroup"); 
       groupTag.Attributes.Add("label", helper.Encode(group.Name)); 
       var optHtml = new StringBuilder(); 
       foreach (var item in group.Items) 
       { 
        var option = new TagBuilder("option"); 
        option.Attributes.Add("value", helper.Encode(item.Value)); 
        if (SelectedValue != null && item.Value == SelectedValue) 
         option.Attributes.Add("selected", "selected"); 
        option.InnerHtml = helper.Encode(item.Text); 
        optHtml.AppendLine(option.ToString(TagRenderMode.Normal)); 
       } 
       groupTag.InnerHtml = optHtml.ToString(); 
       optgroupHtml.AppendLine(groupTag.ToString(TagRenderMode.Normal)); 
      } 
      select.InnerHtml = optgroupHtml.ToString(); 
      return select.ToString(TagRenderMode.Normal); 
     } 
} 

    public class GroupDropListItem 
    { 
     public string Name { get; set; } 
     public List<OptionItem> Items { get; set; } 
    } 

    public class OptionItem 
    { 
     public string Text { get; set; } 
     public string Value { get; set; } 
    } 
} 
+1

После форматирования кода я заметил, что у вас есть дополнительный '}'. Можете ли вы проверить, что я ничего не испортил? Кроме того, это облегчает чтение, если вы используете пробелы вместо вкладок. –

+0

lostway Я использую ваше расширение, но выбранное значение не сохраняется в модели .. любые идеи? Спасибо – beebul

+4

Пожалуйста, см. Http://stackoverflow.com/questions/4142986/optgroup-drop-down-support-in-mvc-problems-with-model-binding для небольшого изменения вашего кода, поскольку я не мог его получить работа ... спасибо. – beebul

91

Моего расширением является немного более сложным, но он имеет все перегруженные как оригинал DropDownList имеет.

На самом деле я создал это специально, чтобы иметь возможность производить DropDownList с группами и трансформироваться в к двум присоединился выбирает с помощью аннотаций jDoubleSelect

using System;using System.Collections;using System.Collections.Generic;using System.Globalization; using System.Linq;using System.Linq.Expressions;using System.Text; using System.Web;using System.Web.Mvc;using System.Web.Routing; 

public class GroupedSelectListItem : SelectListItem 
{ 
    public string GroupKey { get; set; } 
    public string GroupName { get; set; } 
} 

public static class HtmlHelpers 
{ 
    public static MvcHtmlString DropDownGroupList(this HtmlHelper htmlHelper, string name) 
    { 
     return DropDownListHelper(htmlHelper, name, null, null, null); 
    } 

    public static MvcHtmlString DropDownGroupList(this HtmlHelper htmlHelper, string name, IEnumerable<GroupedSelectListItem> selectList) 
    { 
     return DropDownListHelper(htmlHelper, name, selectList, null, null); 
    } 

    public static MvcHtmlString DropDownGroupList(this HtmlHelper htmlHelper, string name, string optionLabel) 
    { 
     return DropDownListHelper(htmlHelper, name, null, optionLabel, null); 
    } 

    public static MvcHtmlString DropDownGroupList(this HtmlHelper htmlHelper, string name, IEnumerable<GroupedSelectListItem> selectList, IDictionary<string, object> htmlAttributes) 
    { 
     return DropDownListHelper(htmlHelper, name, selectList, null, htmlAttributes); 
    } 

    public static MvcHtmlString DropDownGroupList(this HtmlHelper htmlHelper, string name, IEnumerable<GroupedSelectListItem> selectList, object htmlAttributes) 
    { 
     return DropDownListHelper(htmlHelper, name, selectList, null, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); 
    } 

    public static MvcHtmlString DropDownGroupList(this HtmlHelper htmlHelper, string name, IEnumerable<GroupedSelectListItem> selectList, string optionLabel) 
    { 
     return DropDownListHelper(htmlHelper, name, selectList, optionLabel, null); 
    } 

    public static MvcHtmlString DropDownGroupList(this HtmlHelper htmlHelper, string name, IEnumerable<GroupedSelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) 
    { 
     return DropDownListHelper(htmlHelper, name, selectList, optionLabel, htmlAttributes); 
    } 

    public static MvcHtmlString DropDownGroupList(this HtmlHelper htmlHelper, string name, IEnumerable<GroupedSelectListItem> selectList, string optionLabel, object htmlAttributes) 
    { 
     return DropDownListHelper(htmlHelper, name, selectList, optionLabel, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); 
    } 

    public static MvcHtmlString DropDownGroupListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<GroupedSelectListItem> selectList) 
    { 
     return DropDownGroupListFor(htmlHelper, expression, selectList, null /* optionLabel */, null /* htmlAttributes */); 
    } 

    public static MvcHtmlString DropDownGroupListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<GroupedSelectListItem> selectList, object htmlAttributes) 
    { 
     return DropDownGroupListFor(htmlHelper, expression, selectList, null /* optionLabel */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); 
    } 

    public static MvcHtmlString DropDownGroupListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<GroupedSelectListItem> selectList, IDictionary<string, object> htmlAttributes) 
    { 
     return DropDownGroupListFor(htmlHelper, expression, selectList, null /* optionLabel */, htmlAttributes); 
    } 

    public static MvcHtmlString DropDownGroupListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<GroupedSelectListItem> selectList, string optionLabel) 
    { 
     return DropDownGroupListFor(htmlHelper, expression, selectList, optionLabel, null /* htmlAttributes */); 
    } 

    public static MvcHtmlString DropDownGroupListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<GroupedSelectListItem> selectList, string optionLabel, object htmlAttributes) 
    { 
     return DropDownGroupListFor(htmlHelper, expression, selectList, optionLabel, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); 
    } 

    public static MvcHtmlString DropDownGroupListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<GroupedSelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) 
    { 
     if (expression == null) 
     { 
      throw new ArgumentNullException("expression"); 
     } 

     return DropDownListHelper(htmlHelper, ExpressionHelper.GetExpressionText(expression), selectList, optionLabel, htmlAttributes); 
    } 

    private static MvcHtmlString DropDownListHelper(HtmlHelper htmlHelper, string expression, IEnumerable<GroupedSelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) 
    { 
     return SelectInternal(htmlHelper, optionLabel, expression, selectList, false /* allowMultiple */, htmlAttributes); 
    } 


    // Helper methods 

    private static IEnumerable<GroupedSelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name) 
    { 
     object o = null; 
     if (htmlHelper.ViewData != null) 
     { 
      o = htmlHelper.ViewData.Eval(name); 
     } 
     if (o == null) 
     { 
      throw new InvalidOperationException(
       String.Format(
        CultureInfo.CurrentCulture, 
        "Missing Select Data")); 
     } 
     var selectList = o as IEnumerable<GroupedSelectListItem>; 
     if (selectList == null) 
     { 
      throw new InvalidOperationException(
       String.Format(
        CultureInfo.CurrentCulture, 
        "Wrong Select DataType")); 
     } 
     return selectList; 
    } 

    internal static string ListItemToOption(GroupedSelectListItem item) 
    { 
     var builder = new TagBuilder("option") 
     { 
      InnerHtml = HttpUtility.HtmlEncode(item.Text) 
     }; 
     if (item.Value != null) 
     { 
      builder.Attributes["value"] = item.Value; 
     } 
     if (item.Selected) 
     { 
      builder.Attributes["selected"] = "selected"; 
     } 
     return builder.ToString(TagRenderMode.Normal); 
    } 

    private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, string optionLabel, string name, IEnumerable<GroupedSelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes) 
    { 
     name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); 
     if (String.IsNullOrEmpty(name)) 
     { 
      throw new ArgumentException("Null Or Empty", "name"); 
     } 

     bool usedViewData = false; 

     // If we got a null selectList, try to use ViewData to get the list of items. 
     if (selectList == null) 
     { 
      selectList = htmlHelper.GetSelectData(name); 
      usedViewData = true; 
     } 

     object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(name, typeof(string[])) : htmlHelper.GetModelStateValue(name, typeof(string)); 

     // If we haven't already used ViewData to get the entire list of items then we need to 
     // use the ViewData-supplied value before using the parameter-supplied value. 
     if (!usedViewData) 
     { 
      if (defaultValue == null) 
      { 
       defaultValue = htmlHelper.ViewData.Eval(name); 
      } 
     } 

     if (defaultValue != null) 
     { 
      var defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue }; 
      var values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture); 
      var selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase); 
      var newSelectList = new List<GroupedSelectListItem>(); 

      foreach (var item in selectList) 
      { 
       item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text); 
       newSelectList.Add(item); 
      } 
      selectList = newSelectList; 
     } 

     // Convert each ListItem to an <option> tag 
     var listItemBuilder = new StringBuilder(); 

     // Make optionLabel the first item that gets rendered. 
     if (optionLabel != null) 
     { 
      listItemBuilder.AppendLine(ListItemToOption(new GroupedSelectListItem { Text = optionLabel, Value = String.Empty, Selected = false })); 
     } 

     foreach (var group in selectList.GroupBy(i => i.GroupKey)) 
     { 
      string groupName = selectList.Where(i => i.GroupKey == group.Key).Select(it => it.GroupName).FirstOrDefault(); 
      listItemBuilder.AppendLine(string.Format("<optgroup label=\"{0}\" value=\"{1}\">", groupName, group.Key)); 
      foreach (GroupedSelectListItem item in group) 
      { 
       listItemBuilder.AppendLine(ListItemToOption(item)); 
      } 
      listItemBuilder.AppendLine("</optgroup>"); 
     } 

     var tagBuilder = new TagBuilder("select") 
     { 
      InnerHtml = listItemBuilder.ToString() 
     }; 
     tagBuilder.MergeAttributes(htmlAttributes); 
     tagBuilder.MergeAttribute("name", name, true /* replaceExisting */); 
     tagBuilder.GenerateId(name); 
     if (allowMultiple) 
     { 
      tagBuilder.MergeAttribute("multiple", "multiple"); 
     } 

     // If there are any errors for a named field, we add the css attribute. 
     ModelState modelState; 
     if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState)) 
     { 
      if (modelState.Errors.Count > 0) 
      { 
       tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName); 
      } 
     } 

     return MvcHtmlString.Create(tagBuilder.ToString()); 
    } 

    internal static object GetModelStateValue(this HtmlHelper helper, string key, Type destinationType) 
    { 
     ModelState modelState; 
     if (helper.ViewData.ModelState.TryGetValue(key, out modelState)) 
     { 
      if (modelState.Value != null) 
      { 
       return modelState.Value.ConvertTo(destinationType, null /* culture */); 
      } 
     } 
     return null; 
    } 

} 
+0

Удивительный, но один вопрос: эта поддержка проверки данных аннотаций клиента? –

+0

Отличное решение! – Christian13467

+0

Это отличное решение! Благодаря! Бросил прямо в мое приложение MVC. :) –

1

данных для клиента валидаций отсутствующих?

В ответ на вопрос Хроно Любви выше, добавив к решению Сержа Заба в - исправление Milimetric для клиентских проверок MVC3 атрибутов при использовании коллекции выпадающих в том же точке зрения:

 public static MvcHtmlString DropDownGroupListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, 
                     Expression<Func<TModel, TProperty>> 
                      expression, 
                     IEnumerable<GroupedSelectListItem> 
                      selectList, string optionLabel, 
                     IDictionary<string, object> htmlAttributes) 
    { 
     if (expression == null) 
     { 
      throw new ArgumentNullException("expression"); 
     } 

     // fixing clientside validation attributes 
     // http://stackoverflow.com/questions/4799958/asp-net-mvc-3-unobtrusive-client-validation-does-not-work-with-drop-down-lists/8102022#8102022 
     var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 
      var mergedAttributes = 
       htmlHelper.GetUnobtrusiveValidationAttributes(ExpressionHelper.GetExpressionText(expression), metadata); 

      if (htmlAttributes != null) 
      { 
       foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(htmlAttributes)) 
       { 
        object value = descriptor.GetValue(htmlAttributes); 
        mergedAttributes.Add(descriptor.Name, value); 
       } 
      } 

     //return DropDownListHelper(htmlHelper, ExpressionHelper.GetExpressionText(expression), selectList, optionLabel, htmlAttributes); 
     return DropDownListHelper(htmlHelper, ExpressionHelper.GetExpressionText(expression), selectList, optionLabel, mergedAttributes); 
    } 
+0

Ницца, отлично работает, спасибо! – ManicBlowfish

0

В Serge Забе answer, атрибуты данных , как data_valuename, не маржировать в правильной форме data-valuename.

я заменил код tagBuilder.MergeAttributes(htmlAttributes); в SelectInternal способом ответить на этот

foreach (var htmlAttribute in htmlAttributes) 
{ 
    tagBuilder.MergeAttribute(
     htmlAttribute.Key.Replace('_', '-'), 
     (string)htmlAttribute.Value 
    ); 
} 
1

Serge Заб было именно то, что я искал. Будучи живучим VB программист я портировал его на этот VB модуль:

'based on Serge Zab's answer on http://stackoverflow.com/questions/607188/support-for-optgroup-in-dropdownlist-net-mvc 

Imports System.Collections 
Imports System.Collections.Generic 
Imports System.Globalization 
Imports System.Linq 
Imports System.Linq.Expressions 
Imports System.Text 
Imports System.Web 
Imports System.Web.Mvc 
Imports System.Web.Routing 

Public Class GroupedSelectListItem 
    Inherits SelectListItem 
    Public Property GroupKey() As String 
     Get 
      Return m_GroupKey 
     End Get 
     Set(value As String) 
      m_GroupKey = Value 
     End Set 
    End Property 
    Private m_GroupKey As String 
    Public Property GroupName() As String 
     Get 
      Return m_GroupName 
     End Get 
     Set(value As String) 
      m_GroupName = Value 
     End Set 
    End Property 
    Private m_GroupName As String 
End Class 

Public Module HtmlHelpers 
    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupList(htmlHelper As HtmlHelper, name As String) As MvcHtmlString 
     Return DropDownListHelper(htmlHelper, name, Nothing, Nothing, Nothing) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupList(htmlHelper As HtmlHelper, name As String, selectList As IEnumerable(Of GroupedSelectListItem)) As MvcHtmlString 
     Return DropDownListHelper(htmlHelper, name, selectList, Nothing, Nothing) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupList(htmlHelper As HtmlHelper, name As String, optionLabel As String) As MvcHtmlString 
     Return DropDownListHelper(htmlHelper, name, Nothing, optionLabel, Nothing) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupList(htmlHelper As HtmlHelper, name As String, selectList As IEnumerable(Of GroupedSelectListItem), htmlAttributes As IDictionary(Of String, Object)) As MvcHtmlString 
     Return DropDownListHelper(htmlHelper, name, selectList, Nothing, htmlAttributes) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupList(htmlHelper As HtmlHelper, name As String, selectList As IEnumerable(Of GroupedSelectListItem), htmlAttributes As Object) As MvcHtmlString 
     Return DropDownListHelper(htmlHelper, name, selectList, Nothing, New RouteValueDictionary(htmlAttributes)) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupList(htmlHelper As HtmlHelper, name As String, selectList As IEnumerable(Of GroupedSelectListItem), optionLabel As String) As MvcHtmlString 
     Return DropDownListHelper(htmlHelper, name, selectList, optionLabel, Nothing) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupList(htmlHelper As HtmlHelper, name As String, selectList As IEnumerable(Of GroupedSelectListItem), optionLabel As String, htmlAttributes As IDictionary(Of String, Object)) As MvcHtmlString 
     Return DropDownListHelper(htmlHelper, name, selectList, optionLabel, htmlAttributes) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupList(htmlHelper As HtmlHelper, name As String, selectList As IEnumerable(Of GroupedSelectListItem), optionLabel As String, htmlAttributes As Object) As MvcHtmlString 
     Return DropDownListHelper(htmlHelper, name, selectList, optionLabel, New RouteValueDictionary(htmlAttributes)) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupListFor(Of TModel, TProperty)(htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TProperty)), selectList As IEnumerable(Of GroupedSelectListItem)) As MvcHtmlString 
     ' optionLabel 
     ' htmlAttributes 
     Return DropDownGroupListFor(htmlHelper, expression, selectList, Nothing, Nothing) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupListFor(Of TModel, TProperty)(htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TProperty)), selectList As IEnumerable(Of GroupedSelectListItem), htmlAttributes As Object) As MvcHtmlString 
     ' optionLabel 
     Return DropDownGroupListFor(htmlHelper, expression, selectList, Nothing, New RouteValueDictionary(htmlAttributes)) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupListFor(Of TModel, TProperty)(htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TProperty)), selectList As IEnumerable(Of GroupedSelectListItem), htmlAttributes As IDictionary(Of String, Object)) As MvcHtmlString 
     ' optionLabel 
     Return DropDownGroupListFor(htmlHelper, expression, selectList, Nothing, htmlAttributes) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupListFor(Of TModel, TProperty)(htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TProperty)), selectList As IEnumerable(Of GroupedSelectListItem), optionLabel As String) As MvcHtmlString 
     ' htmlAttributes 
     Return DropDownGroupListFor(htmlHelper, expression, selectList, optionLabel, Nothing) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupListFor(Of TModel, TProperty)(htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TProperty)), selectList As IEnumerable(Of GroupedSelectListItem), optionLabel As String, htmlAttributes As Object) As MvcHtmlString 
     Return DropDownGroupListFor(htmlHelper, expression, selectList, optionLabel, New RouteValueDictionary(htmlAttributes)) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Public Function DropDownGroupListFor(Of TModel, TProperty)(htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TProperty)), selectList As IEnumerable(Of GroupedSelectListItem), optionLabel As String, htmlAttributes As IDictionary(Of String, Object)) As MvcHtmlString 
     If expression Is Nothing Then 
      Throw New ArgumentNullException("expression") 
     End If 

     Return DropDownListHelper(htmlHelper, ExpressionHelper.GetExpressionText(expression), selectList, optionLabel, htmlAttributes) 
    End Function 

    Private Function DropDownListHelper(htmlHelper As HtmlHelper, expression As String, selectList As IEnumerable(Of GroupedSelectListItem), optionLabel As String, htmlAttributes As IDictionary(Of String, Object)) As MvcHtmlString 
     ' allowMultiple 
     Return SelectInternal(htmlHelper, optionLabel, expression, selectList, False, htmlAttributes) 
    End Function 


    ' Helper methods 

    <System.Runtime.CompilerServices.Extension> _ 
    Private Function GetSelectData(htmlHelper As HtmlHelper, name As String) As IEnumerable(Of GroupedSelectListItem) 
     Dim o As Object = Nothing 
     If htmlHelper.ViewData IsNot Nothing Then 
      o = htmlHelper.ViewData.Eval(name) 
     End If 
     If o Is Nothing Then 
      Throw New InvalidOperationException([String].Format(CultureInfo.CurrentCulture, "Missing Select Data", name, "IEnumerable<GroupedSelectListItem>")) 
     End If 
     Dim selectList As IEnumerable(Of GroupedSelectListItem) = TryCast(o, IEnumerable(Of GroupedSelectListItem)) 
     If selectList Is Nothing Then 
      Throw New InvalidOperationException([String].Format(CultureInfo.CurrentCulture, "Wrong Select DataType", name, o.[GetType]().FullName, "IEnumerable<GroupedSelectListItem>")) 
     End If 
     Return selectList 
    End Function 

    Friend Function ListItemToOption(item As GroupedSelectListItem) As String 
     Dim builder As New TagBuilder("option") With { _ 
      .InnerHtml = HttpUtility.HtmlEncode(item.Text) _ 
     } 
     If item.Value IsNot Nothing Then 
      builder.Attributes("value") = item.Value 
     End If 
     If item.Selected Then 
      builder.Attributes("selected") = "selected" 
     End If 
     Return builder.ToString(TagRenderMode.Normal) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Private Function SelectInternal(htmlHelper__1 As HtmlHelper, optionLabel As String, name As String, selectList As IEnumerable(Of GroupedSelectListItem), allowMultiple As Boolean, htmlAttributes As IDictionary(Of String, Object)) As MvcHtmlString 
     name = htmlHelper__1.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name) 
     If [String].IsNullOrEmpty(name) Then 
      Throw New ArgumentException("Null Or Empty", "name") 
     End If 

     Dim usedViewData As Boolean = False 

     ' If we got a null selectList, try to use ViewData to get the list of items. 
     If selectList Is Nothing Then 
      selectList = htmlHelper__1.GetSelectData(name) 
      usedViewData = True 
     End If 

     Dim defaultValue As Object = If((allowMultiple), htmlHelper__1.GetModelStateValue(name, GetType(String())), htmlHelper__1.GetModelStateValue(name, GetType(String))) 

     ' If we haven't already used ViewData to get the entire list of items then we need to 
     ' use the ViewData-supplied value before using the parameter-supplied value. 
     If Not usedViewData Then 
      If defaultValue Is Nothing Then 
       defaultValue = htmlHelper__1.ViewData.Eval(name) 
      End If 
     End If 

     If defaultValue IsNot Nothing Then 
      Dim defaultValues As IEnumerable = If((allowMultiple), TryCast(defaultValue, IEnumerable), New String() {defaultValue}) 
      Dim values As IEnumerable(Of String) = From value In defaultValues Select (Convert.ToString(value, CultureInfo.CurrentCulture)) 
      Dim selectedValues As New HashSet(Of String)(values, StringComparer.OrdinalIgnoreCase) 
      Dim newSelectList As New List(Of GroupedSelectListItem)() 

      For Each item As GroupedSelectListItem In selectList 
       item.Selected = If((item.Value IsNot Nothing), selectedValues.Contains(item.Value), selectedValues.Contains(item.Text)) 
       newSelectList.Add(item) 
      Next 
      selectList = newSelectList 
     End If 

     ' Convert each ListItem to an <option> tag 
     Dim listItemBuilder As New StringBuilder() 

     ' Make optionLabel the first item that gets rendered. 
     If optionLabel IsNot Nothing Then 
      listItemBuilder.AppendLine(ListItemToOption(New GroupedSelectListItem() With { _ 
       .Text = optionLabel, _ 
       .Value = [String].Empty, _ 
       .Selected = False _ 
      })) 
     End If 

     For Each group As Object In selectList.GroupBy(Function(i) i.GroupKey) 
      Dim groupName As String = selectList.Where(Function(i) i.GroupKey = group.Key).[Select](Function(it) it.GroupName).FirstOrDefault() 
      listItemBuilder.AppendLine(String.Format("<optgroup label=""{0}"" value=""{1}"">", groupName, group.Key)) 
      For Each item As GroupedSelectListItem In group 
       listItemBuilder.AppendLine(ListItemToOption(item)) 
      Next 
      listItemBuilder.AppendLine("</optgroup>") 
     Next 

     Dim tagBuilder As New TagBuilder("select") With { _ 
      .InnerHtml = listItemBuilder.ToString() _ 
     } 
     TagBuilder.MergeAttributes(htmlAttributes) 
     ' replaceExisting 
     TagBuilder.MergeAttribute("name", name, True) 
     TagBuilder.GenerateId(name) 
     If allowMultiple Then 
      TagBuilder.MergeAttribute("multiple", "multiple") 
     End If 

     ' If there are any errors for a named field, we add the css attribute. 
     Dim modelState As ModelState = Nothing 
     If htmlHelper__1.ViewData.ModelState.TryGetValue(name, modelState) Then 
      If modelState.Errors.Count > 0 Then 
       TagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName) 
      End If 
     End If 

     Return MvcHtmlString.Create(TagBuilder.ToString()) 
    End Function 

    <System.Runtime.CompilerServices.Extension> _ 
    Friend Function GetModelStateValue(helper As HtmlHelper, key As String, destinationType As Type) As Object 
     Dim modelState As ModelState = Nothing 
     If helper.ViewData.ModelState.TryGetValue(key, modelState) Then 
      If modelState.Value IsNot Nothing Then 
       ' culture 
       Return modelState.Value.ConvertTo(destinationType, Nothing) 
      End If 
     End If 
     Return Nothing 
    End Function 

End Module 
1

Я пытался решением @Serge Заба из которых работает хорошо, но имел некоторые проблемы с ненавязчивой проверкой, после некоторого осмотра я обнаружил проблему ,

Там, кажется, некоторые обязательные атрибуты отсутствуют Офф-выберите элемент, если вы хотите, чтобы проверка Jquery стрелять, только после создания TagBuilder

TagBuilder tagBuilder = new TagBuilder("select"); 

Добавить эти атрибуты

tagBuilder.MergeAttribute("data-val", "true",true); 
tagBuilder.MergeAttribute("data-val-required", "your validation message", true) 

и ненавязчивая проверка должна срабатывать.

0

Только примечание об использовании расширения Сержа для создания более одного списка выбора в той же форме.У меня возникли проблемы с получением второго списка выбора для применения групп, и когда я исследовал генерируемый html, я понял, что оба имеют одинаковый идентификатор. Чтобы исправить это, перейдите в функцию SelectInternal в расширении serge и закомментируйте/удалите следующие две строки:

tagBuilder.MergeAttribute ("name", name, true/* replaceExisting * /); tagBuilder.GenerateId (имя);

В качестве альтернативы вы можете просто передать уникальные идентификаторы для каждого (хотя DropDownGroupListFor не принимает параметр «имя строки», поэтому вам придется добавить перегрузку, которая делает)

34

Это был добавлен в ASP.Net MVC версии 5.2 и теперь встроен.

Group property on SelectListItem позволяет указать группу для каждого элемента:

Новая SelectList constructors также позволяет указать имя поля, которое содержит название группы на прилагаемом списке элементов.

HtmlHelper DropDownList и DropDownListДля методов теперь генерируются элементы optgroup на основе групп, включенных в список элементов.

Легко!

+3

Да! И один пример здесь: http://qiita.com/rryu/items/0fdfde55a62a44f0add0 var items = new List (); var group1 = new SelectListGroup() {Name = "Group 1"}; items.Add (новый SelectListItem() {Text = "Item1", Group = group1}); и @ Html.DropDownList («select», items) – clement

+0

это работает очень хорошо. –

+0

Отличный ответ, рад видеть, что эта функция включена! – Dal

0

Мне нужно решение для обработки множественного выбора с optgroup, и я использовал решение Serge Zab. У меня всего два комментария об этом (слишком долго для комментария).

  • Несмотря на его заявление, его решение не поддерживает все существующие перегруженные DropDownListFor, так как он не поддерживает MultiSelectList или SelectList, часто используемый в моделях. Но это не сложно добавить.

  • Его решение не работало для меня для множественного выбора, чтобы инициализировать выбранные/не выбранные элементы из модели: исходные значения не были затронуты. Я только изменил следующий метод:

    частного статического MvcHtmlString DropDownListHelper (...) { возвращения SelectInternal (HtmlHelper, optionLabel, выражение, SelectList, ложь/* AllowMultiple * /, htmlAttributes); }

Для этого:

private static MvcHtmlString DropDownListHelper(HtmlHelper htmlHelper, string expression, IEnumerable<GroupedSelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) 
{ 
    bool allowMultiple = htmlAttributes.ContainsKey("multiple"); 
    return SelectInternal(htmlHelper, optionLabel, expression, selectList, allowMultiple, htmlAttributes); 
} 

И это сработало, как ожидалось. Конечно множественный атрибут должен быть определен: @ Html.DropDownGroupListFor (м => m.Selected, Model.Values, новый {множественного = "несколько"})

Благодаря Serge для своего ответа.