在下拉列表.NET MVC中支持optgroup?

时间:2009-03-03 16:46:06

标签: asp.net-mvc drop-down-menu

继续这个问题programmatically creating a drop down list我希望我的列表也有几个optgroup列表。这目前可能吗?

我知道我需要将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>

10 个答案:

答案 0 :(得分:92)

我的扩展程序有点复杂,但它具有原始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;
    }

}

答案 1 :(得分:36)

这是在5.2版本的ASP.Net MVC中添加的,现在是内置的。

Group property on SelectListItem允许您为每个项目指定一个组:

SelectList constructors还允许您在提供的项目列表中提供包含组标题的字段的名称。

HtmlHelper DropDownList和DropDownListFor方法现在根据项目列表中包含的组生成optgroup元素。

容易!

答案 2 :(得分: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; }
    }
}

答案 3 :(得分:13)

查看www.codeplex.com/aspnet上的代码,似乎没有SelectList和DropDownList扩展方法都支持在select中使用OptGroup。看起来您需要编写自己的扩展方法并扩展SelectListItem以包含分组或在标记中手动生成选择。

答案 4 :(得分:1)

缺少客户端验证的数据注释?

回答上面的Chrno Love的问题,添加了Serge Zab的解决方案 - 在同一视图中使用一组下拉列表时,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);
    }

答案 5 :(得分:1)

Serge Zab的回答正是我所寻找的。作为一个死硬的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

答案 6 :(得分:1)

我一直在尝试使用@Serge Zab解决方案,该方法运行良好,但在进行一些检查后发现了问题,但是在不显眼的验证方面遇到了一些麻烦。

如果您希望在创建TagBuilder之后触发Jquery验证,那么select元素似乎缺少一些必需的属性

TagBuilder tagBuilder = new TagBuilder("select");

添加这些属性

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

并且应该触发不引人注目的验证。

答案 7 :(得分:0)

在Serge Zab answer中,数据属性(如data_valuename)的边距不正确data-valuename

我将tagBuilder.MergeAttributes(htmlAttributes);方法中的代码SelectInternal替换为此

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

答案 8 :(得分:0)

关于使用Serge的扩展名在同一表单上创建多个选择列表的注释。我在获取第二个选择列表以应用组时遇到问题,当我检查生成的html时,我意识到两者都被赋予了相同的ID。要解决此问题,请进入serge扩展中的SelectInternal函数并注释掉/删除以下两行:

tagBuilder.MergeAttribute(“name”,name,true / * replaceExisting * /); tagBuilder.GenerateId(名称);

或者你可以将唯一的id传递给每个(虽然DropDownGroupListFor没有采用'string name'参数,因此你必须添加一个重载)

答案 9 :(得分:0)

我需要一个解决方案来处理optgroup的多项选择,我使用了Serge Zab解决方案。我只有两条评论(评论时间太长)。

  • 尽管他声称,他的解决方案并不支持DropDownListFor的所有现有重载,因为它不支持通常在模型中使用的MultiSelectList或SelectList。但是添加这些并不难。

  • 他的解决方案不适用于多重选择来初始化模型中的选定/未选定项目:初始值不受影响。 我刚刚修改了以下方法:

    private static MvcHtmlString DropDownListHelper(...) {     return SelectInternal(htmlHelper,optionLabel,expression,selectList,false / * allowMultiple * /,htmlAttributes); }

对此:

TextView

它按预期工作。 当然必须定义multiple属性: @ Html.DropDownGroupListFor(m =&gt; m.Selected,Model.Values,new {multiple =&#34; multiple&#34;})

感谢Serge的回答。