2009-03-03 12 views
62

Continuando da questa domanda programmatically creating a drop down list Vorrei che la mia lista contenga più elenchi optgroup. Questo è attualmente possibile?Supporto per optgroup in dropdownlist .NET MVC?

So che ho bisogno di passare una selectList a dropDownList ma non so come aggiungere testo, valore, optgroup alla selectList.

Voglio che il risultato finale per la produzione di:

<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

Ora integrato in ASP.Net MVC versione 5.2 in poi - vedere la mia risposta sotto –

risposta

13

Guardando attraverso il codice a www.codeplex.com/aspnet, non sembra che né il SelectList né il metodo di estensione DropDownList supporta l'uso di OPTGROUP in un prescelto. Sembra che dovrai scrivere il tuo metodo di estensione ed estendere SelectListItem per contenere il raggruppamento o generare manualmente la selezione nel markup.

25

ho solo scrivere un estensioni per fare questo, vederlo:


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

Dopo aver formattato il codice, ho notato che avete un '}' extra. Puoi controllare che non abbia rovinato qualcosa? Inoltre, aiuta molto la leggibilità se si utilizzano spazi anziché schede. –

+0

lostway Sto usando la tua estensione ma il valore selezionato non viene salvato nel modello .. qualche idea? Grazie – beebul

+4

Si prega di consultare http://stackoverflow.com/questions/4142986/optgroup-drop-down-support-in-mvc-problems-with-model-binding per una piccola modifica al codice in quanto non sono riuscito a farlo lavoro ... grazie. – beebul

91

mia estensione è un po 'più complicato, ma ha tutti i sovraccarichi come DropDownList originale ha.

In realtà ho creato lo speciale di essere in grado di produrre DropDownList con i gruppi e per essere trasformato in due si è unito seleziona con l'aiuto di annotazioni 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

Impressionante, ma una domanda: questo supporto per la convalida del client delle annotazioni di dati? –

+0

Ottima soluzione! – Christian13467

+0

Questa è un'ottima soluzione! Grazie! Dropped nella mia app MVC. :) –

1

dati per le convalide client mancanti?

In risposta alla domanda di Chrno Amore sopra, aggiungendo alla soluzione di Serge Zab - correzione di millimetrica per MVC3 validazione lato client attributi quando si utilizza un insieme di menù a discesa nella stessa vista:

 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

Bello, funziona alla grande, grazie! – ManicBlowfish

0

In Serge Zab answer, attributi dei dati , come data_valuename, non marging nella forma corretta data-valuename.

ho sostituito il codice tagBuilder.MergeAttributes(htmlAttributes); in SelectInternal metodo per la risposta di questo

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

Serge Zab era esattamente quello che stavo cercando. Essendo un dado programmatore di VB difficile ho portato a questo modulo 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

Ho cercato soluzione @Serge Zab fuori che funziona bene, ma stavo avendo qualche problema con la convalida discreto, dopo un po 'l'ispezione ho trovato il problema .

Sembra esserci alcuni attributi obbligatori mancanti fuori l'elemento di selezione se si desidera che la convalida Jquery al fuoco, solo dopo aver creato il vostro TagBuilder

TagBuilder tagBuilder = new TagBuilder("select"); 

Aggiungi questi attributi

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

e la convalida non invadente dovrebbe sparare.

0

Solo una nota sull'utilizzo dell'estensione di Serge per creare più di una lista di selezione sullo stesso modulo.Stavo avendo problemi a ottenere la seconda lista di selezione per applicare i gruppi, e quando ho esaminato l'html generato ho capito che a entrambi era stato dato lo stesso id. Per risolvere questo problema, vai nella funzione SelectInternal nell'estensione di serge e commenta/elimina le seguenti due righe:

tagBuilder.MergeAttribute ("nome", nome, vero/* replaceExisting * /); tagBuilder.GenerateId (nome);

In alternativa si può semplicemente passare id uniche a ciascuno (anche se il DropDownGroupListFor non prendere il parametro 'nome della stringa' quindi dovrete aggiungere un sovraccarico che fa)

34

Questo è stato aggiunto al ASP.Net MVC alla versione 5.2 ed è ora integrato.

Il Group property on SelectListItem consente di specificare un gruppo per ogni elemento:

Nuova SelectList constructors permettono anche di fornire il nome del campo che contiene il titolo del gruppo nella lista fornita di elementi.

I metodi HtmlHelper DropDownList e DropDownListFor ora generano elementi optgroup in base ai gruppi inclusi nell'elenco di elementi.

Facile!

+3

Sì! E un esempio qui: http://qiita.com/rryu/items/0fdfde55a62a44f0add0 var items = new List (); var group1 = new SelectListGroup() {Name = "Gruppo 1"}; items.Add (new SelectListItem() {Text = "Item1", Group = group1}); e @ Html.DropDownList ("select", items) – clement

+0

funziona molto bene. –

+0

Ottima risposta, felice di vedere questa funzionalità è stata inclusa ora! – Dal

0

Avevo bisogno di una soluzione per gestire una selezione multipla con optgroup, e ho usato la soluzione Serge Zab. Ho solo due commenti a riguardo (troppo lungo per un commento).

  • Nonostante le sue affermazioni, la sua soluzione non supporta tutti i sovraccarichi esistenti di DropDownListFor, dal momento che non supporta MultiSelectList o SelectList, oftenly utilizzato nei modelli. Ma non è difficile aggiungerli.

  • La sua soluzione non funzionava per la selezione multipla per inizializzare gli elementi selezionati/non selezionati dal modello: i valori iniziali non erano interessati. Ho appena modificato il seguente metodo:

    private static MvcHtmlString DropDownListHelper (...) { ritorno SelectInternal (HtmlHelper, optionLabel, espressione, SelectList, false/* AllowMultiple * /, htmlAttributes); }

A tal:

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

E ha funzionato come previsto. Naturalmente l'attributo multipla deve essere definito: @ Html.DropDownGroupListFor (m => m.Selected, Model.Values, new {multipla = "multipli"})

Grazie a Serge per la sua risposta.