web-dev-qa-db-fra.com

Prise en charge de optgroup dans la liste déroulante .NET MVC?

En continuant à partir de cette question création par programme d'une liste déroulante je voudrais que ma liste contienne également plusieurs listes optgroup. Est-ce actuellement possible?

Je sais que je dois passer un selectList à un dropDownList mais je ne sais pas comment ajouter du texte, une valeur, optgroup à la selectList.

Je veux que le résultat final produise:

<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>
63
KevinUK

En regardant à travers le code sur www.codeplex.com/aspnet , il ne semble pas que ni la méthode d'extension SelectList ni l'extension DropDownList ne prennent en charge l'utilisation d'OptGroup dans une sélection. Il semble que vous deviez écrire votre propre méthode d'extension et étendre SelectListItem pour contenir le groupement ou générer manuellement la sélection dans le balisage.

13
tvanfosson

Mon extension est un peu plus compliquée, mais toutes les surcharges sont identiques à celles de DropDownList.

En fait, je l’ai spécialement créé pour pouvoir produire DropDownList avec des groupes et pour être transformé en deux sélections jointes à l’aide de 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;
    }

}
92
Serge Zab

Cela a été ajouté à ASP.Net MVC à la version 5.2 et est maintenant intégré.

La propriété Group sur SelectListItem vous permet de spécifier un groupe pour chaque élément:

Les nouveaux constructeurs SelectList vous permettent également de fournir le nom du champ contenant le titre du groupe dans la liste d'éléments fournie.

Les méthodes HtmlHelper DropDownList et DropDownListFor génèrent désormais des éléments optgroup basés sur les groupes inclus dans la liste d'éléments.

Facile!

35
Dan Malcolm

Je viens d'écrire une extension pour le faire, voyez-le:


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; }
    }
}
25
lostway

La réponse de Serge Zab était exactement ce que je cherchais. Etant un programmeur infatigable VB, je l'ai porté dans ce module 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
Daz

J'ai essayé la solution @Serge Zab qui fonctionne bien mais qui rencontrait des problèmes avec la validation discrète. Après une inspection, j'ai trouvé le problème.

Il semble que certains attributs requis manquent dans l'élément select si vous souhaitez que la validation Jquery soit activée, juste après la création de votre TagBuilder.

TagBuilder tagBuilder = new TagBuilder("select");

Ajouter ces attributs

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

et la validation discrète doit être déclenchée.

1
Troublesum

Annotations de données pour les validations client manquantes?

En réponse à la question ci-dessus de Chrno Love, ajout à la solution de Serge Zab - correctif de Milimetric pour les attributs de validation côté client MVC3 lors de l'utilisation d'une collection de menus déroulants dans la même vue:

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

J'avais besoin d'une solution pour gérer une sélection multiple avec optgroup, et j'ai utilisé la solution Serge Zab. Je n'ai que deux commentaires à ce sujet (trop long pour un commentaire).

  • Malgré ses affirmations, sa solution ne prend pas en charge toutes les surcharges existantes de DropDownListFor, car elle ne prend pas en charge MultiSelectList ou SelectList, souvent utilisé dans les modèles. Mais ce n'est pas difficile à ajouter.

  • Sa solution ne fonctionnait pas pour moi pour la sélection multiple pour initialiser les éléments sélectionnés/non sélectionnés du modèle: les valeurs initiales n'étaient pas affectées. Je viens de modifier la méthode suivante: 

    statique privée MvcHtmlString DropDownListHelper (...) { return SelectInternal (htmlHelper, optionLabel, expression, selectList, false/* allowMultiple * /, htmlAttributes); }

Pour ça :

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

Et cela a fonctionné comme prévu. Bien sûr, l'attribut multiple doit être défini: @ Html.DropDownGroupListFor (m => m.Selected, Model.Values, new {multiple = "multiple"})

Merci à Serge pour sa réponse.

0
AFract

Juste une note sur l'utilisation de l'extension de Serge pour créer plusieurs listes de sélection sur le même formulaire. J'avais des problèmes pour obtenir la deuxième liste de sélection pour appliquer les groupes, et lorsque j'ai examiné le code HTML généré, j'ai réalisé que le même identifiant avait été attribué aux deux. Pour résoudre ce problème, accédez à la fonction SelectInternal de l'extension serge et commentez/supprimez les deux lignes suivantes:

tagBuilder.MergeAttribute ("nom", nom, true/* replaceExisting * /); tagBuilder.GenerateId (nom);

Sinon, vous pouvez simplement passer des identifiants uniques à chacun (bien que DropDownGroupListFor ne prenne pas le paramètre 'nom de chaîne', vous devrez donc ajouter une surcharge qui le fera)

0
Aleksandr Albert

Dans Serge Zab answer , les attributs de données, tels que data_valuename, ne marquent pas sous la forme correcte data-valuename

J'ai remplacé le code tagBuilder.MergeAttributes(htmlAttributes); dans la méthode SelectInternal par cette méthode.

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