7.14.2010

MVC 2 DropDownListFor - Any troubles? - Part 2

Hello again. Today I want to talk about select list with optgroups. So, I need such a list. MVC has no support for it. What to do?

After a little browsing I decide wrote my own solution. It is based on code from fixed version of MVC DropDownList (you can read about it in Part 1).

Main features of the list:
of course - support of groups of options
you can use it with options and with groups of options in any order, iow first item can be option, then - option group, and at the end, again - option.
Lets go to quick code-review: option and group of options (SelectListItem3Option, SelectListItem3OptionGroup) inherited from base class SelectListItem3. So all collections you can create as collections of this base class and store options and optgroups simultaneously. All extension methods has suffix "3". List of extension methods are the same as in MVC.

In general - thats all. Enjoy yourself! All required code listed below:

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

namespace HennadiyKurabko.Web.Mvc.Html
{
    /// <summary>Represents support for making selections in a list.</summary>
    public static class SelectExtensions3
    {
        /// <summary>Returns a single-selection select element using the specified HTML helper and the name of the form field.</summary>
        /// <returns>An HTML select element.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString DropDownList3(this HtmlHelper htmlHelper, string name)
        {
            return htmlHelper.DropDownList3(name, null, null, ((IDictionary<string, object>)null));
        }

        /// <summary>Returns a single-selection select element using the specified HTML helper, the name of the form field, and the specified list items.</summary>
        /// <returns>An HTML select element with an option subelement for each item in the list.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString DropDownList3(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem3> selectList)
        {
            return htmlHelper.DropDownList3(name, selectList, null, ((IDictionary<string, object>)null));
        }

        /// <summary>Returns a single-selection select element using the specified HTML helper, the name of the form field, and an option label.</summary>
        /// <returns>An HTML select element with an option subelement for each item in the list.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <param name="optionLabel">The text for a default empty item. This parameter can be null.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString DropDownList3(this HtmlHelper htmlHelper, string name, string optionLabel)
        {
            return htmlHelper.DropDownList3(name, null, optionLabel, ((IDictionary<string, object>)null));
        }

        /// <summary>Returns a single-selection select element using the specified HTML helper, the name of the form field, the specified list items, and the specified HTML attributes.</summary>
        /// <returns>An HTML select element with an option subelement for each item in the list.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString DropDownList3(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem3> selectList, IDictionary<string, object> htmlAttributes)
        {
            return htmlHelper.DropDownList3(name, selectList, null, htmlAttributes);
        }

        /// <summary>Returns a single-selection select element using the specified HTML helper, the name of the form field, the specified list items, and the specified HTML attributes.</summary>
        /// <returns>An HTML select element with an option subelement for each item in the list.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString DropDownList3(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem3> selectList, object htmlAttributes)
        {
            return htmlHelper.DropDownList3(name, selectList, null, ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));
        }

        /// <summary>Returns a single-selection select element using the specified HTML helper, the name of the form field, the specified list items, and an option label.</summary>
        /// <returns>An HTML select element with an option subelement for each item in the list.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <param name="optionLabel">The text for a default empty item. This parameter can be null.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString DropDownList3(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem3> selectList, string optionLabel)
        {
            return htmlHelper.DropDownList3(name, selectList, optionLabel, ((IDictionary<string, object>)null));
        }

        /// <summary>Returns a single-selection select element using the specified HTML helper, the name of the form field, the specified list items, an option label, and the specified HTML attributes.</summary>
        /// <returns>An HTML select element with an option subelement for each item in the list.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <param name="optionLabel">The text for a default empty item. This parameter can be null.</param>
        /// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString DropDownList3(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem3> selectList, string optionLabel, IDictionary<string, object> htmlAttributes)
        {
            return DropDownList3Helper(htmlHelper, name, selectList, optionLabel, htmlAttributes);
        }

        /// <summary>Returns a single-selection select element using the specified HTML helper, the name of the form field, the specified list items, an option label, and the specified HTML attributes.</summary>
        /// <returns>An HTML select element with an option subelement for each item in the list.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <param name="optionLabel">The text for a default empty item. This parameter can be null.</param>
        /// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString DropDownList3(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem3> selectList, string optionLabel, object htmlAttributes)
        {
            return htmlHelper.DropDownList3(name, selectList, optionLabel, ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));
        }

        /// <summary>Returns an HTML select element for each property in the object that is represented by the specified expression using the specified list items.</summary>
        /// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the value.</typeparam>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="expression" /> parameter is null.</exception>
        public static MvcHtmlString DropDownList3For<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem3> selectList)
        {
            return htmlHelper.DropDownList3For<TModel, TProperty>(expression, selectList, null, ((IDictionary<string, object>)null));
        }

        /// <summary>Returns an HTML select element for each property in the object that is represented by the specified expression using the specified list items and HTML attributes.</summary>
        /// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <param name="htmlAttributes">A dictionary that contains the HTML attributes to set for the element.</param>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the value.</typeparam>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="expression" /> parameter is null.</exception>
        public static MvcHtmlString DropDownList3For<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem3> selectList, IDictionary<string, object> htmlAttributes)
        {
            return htmlHelper.DropDownList3For<TModel, TProperty>(expression, selectList, null, htmlAttributes);
        }

        /// <summary>Returns an HTML select element for each property in the object that is represented by the specified expression using the specified list items and HTML attributes.</summary>
        /// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the value.</typeparam>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="expression" /> parameter is null.</exception>
        public static MvcHtmlString DropDownList3For<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem3> selectList, object htmlAttributes)
        {
            return htmlHelper.DropDownList3For<TModel, TProperty>(expression, selectList, null, ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));
        }

        /// <summary>Returns an HTML select element for each property in the object that is represented by the specified expression using the specified list items and option label.</summary>
        /// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <param name="optionLabel">The text for a default empty item. This parameter can be null.</param>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the value.</typeparam>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="expression" /> parameter is null.</exception>
        public static MvcHtmlString DropDownList3For<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem3> selectList, string optionLabel)
        {
            return htmlHelper.DropDownList3For<TModel, TProperty>(expression, selectList, optionLabel, ((IDictionary<string, object>)null));
        }

        /// <summary>Returns an HTML select element for each property in the object that is represented by the specified expression using the specified list items, option label, and HTML attributes.</summary>
        /// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <param name="optionLabel">The text for a default empty item. This parameter can be null.</param>
        /// <param name="htmlAttributes">A dictionary that contains the HTML attributes to set for the element.</param>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the value.</typeparam>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="expression" /> parameter is null.</exception>
        public static MvcHtmlString DropDownList3For<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem3> selectList, string optionLabel, IDictionary<string, object> htmlAttributes)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }
            return DropDownList3Helper(htmlHelper, ExpressionHelper.GetExpressionText(expression), selectList, optionLabel, htmlAttributes);
        }

        /// <summary>Returns an HTML select element for each property in the object that is represented by the specified expression using the specified list items, option label, and HTML attributes.</summary>
        /// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3" /> objects that are used to populate the drop-down list.</param>
        /// <param name="optionLabel">The text for a default empty item. This parameter can be null.</param>
        /// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the value.</typeparam>
        public static MvcHtmlString DropDownList3For<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem3> selectList, string optionLabel, object htmlAttributes)
        {
            return htmlHelper.DropDownList3For<TModel, TProperty>(expression, selectList, optionLabel, ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));
        }

        /// <summary>Returns a multi-select select element using the specified HTML helper and the name of the form field.</summary>
        /// <returns>An HTML select element.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString ListBox3(this HtmlHelper htmlHelper, string name)
        {
            return htmlHelper.ListBox3(name, null, ((IDictionary<string, object>)null));
        }

        /// <summary>Returns a multi-select select element using the specified HTML helper, the name of the form field, and the specified list items.</summary>
        /// <returns>An HTML select element with an option subelement for each item in the list.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3Option" /> objects that are used to populate the drop-down list.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString ListBox3(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem3Option> selectList)
        {
            return htmlHelper.ListBox3(name, selectList, ((IDictionary<string, object>)null));
        }

        /// <summary>Returns a multi-select select element using the specified HTML helper, the name of the form field, the specified list items, and the specified HMTL attributes.</summary>
        /// <returns>An HTML select element with an option subelement for each item in the list..</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3Option" /> objects that are used to populate the drop-down list.</param>
        /// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString ListBox3(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem3Option> selectList, IDictionary<string, object> htmlAttributes)
        {
            return ListBox3Helper(htmlHelper, name, selectList, htmlAttributes);
        }

        /// <summary>Returns a multi-select select element using the specified HTML helper, the name of the form field, and the specified list items.</summary>
        /// <returns>An HTML select element with an option subelement for each item in the list..</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="name">The name of the form field to return.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3Option" /> objects that are used to populate the drop-down list.</param>
        /// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
        /// <exception cref="T:System.ArgumentException">The <paramref name="name" /> parameter is null or empty.</exception>
        public static MvcHtmlString ListBox3(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem3Option> selectList, object htmlAttributes)
        {
            return htmlHelper.ListBox3(name, selectList, ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));
        }

        /// <summary>Returns an HTML select element for each property in the object that is represented by the specified expression and using the specified list items.</summary>
        /// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3Option" /> objects that are used to populate the list.</param>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the value.</typeparam>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="expression" /> parameter is null.</exception>
        public static MvcHtmlString ListBox3For<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem3Option> selectList)
        {
            return htmlHelper.ListBox3For<TModel, TProperty>(expression, selectList, ((IDictionary<string, object>)null));
        }

        /// <summary>Returns an HTML select element for each property in the object that is represented by the specified expression using the specified list items and HTML attributes.</summary>
        /// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3Option" /> objects that are used to populate the list.</param>
        /// <param name="htmlAttributes">A dictionary that contains the HTML attributes to set for the element.</param>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the value.</typeparam>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="expression" /> parameter is null.</exception>
        public static MvcHtmlString ListBox3For<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem3Option> selectList, IDictionary<string, object> htmlAttributes)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }
            return ListBox3Helper(htmlHelper, ExpressionHelper.GetExpressionText(expression), selectList, htmlAttributes);
        }

        /// <summary>Returns an HTML select element for each property in the object that is represented by the specified expression using the specified list items and HTML attributes.</summary>
        /// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
        /// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
        /// <param name="expression">An expression that identifies the object that contains the properties to render.</param>
        /// <param name="selectList">A collection of <see cref="T:System.Web.Mvc.SelectListItem3Option" /> objects that are used to populate the list.</param>
        /// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the value.</typeparam>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="expression" /> parameter is null.</exception>
        public static MvcHtmlString ListBox3For<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem3Option> selectList, object htmlAttributes)
        {
            return htmlHelper.ListBox3For<TModel, TProperty>(expression, selectList, ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));
        }

        private static MvcHtmlString ListBox3Helper(HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem3Option> selectList, IDictionary<string, object> htmlAttributes)
        {
            return htmlHelper.SelectInternal3(null, name, selectList.Cast<SelectListItem3>(), true, htmlAttributes);
        }

        private static MvcHtmlString DropDownList3Helper(HtmlHelper htmlHelper, string expression, IEnumerable<SelectListItem3> selectList, string optionLabel, IDictionary<string, object> htmlAttributes)
        {
            return htmlHelper.SelectInternal3(optionLabel, expression, selectList, false, htmlAttributes);
        }

        private static MvcHtmlString SelectInternal3(this HtmlHelper htmlHelper, string optionLabel, string name, IEnumerable<SelectListItem3> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes)
        {
            string shortName = name;
            string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(shortName);

            if (string.IsNullOrEmpty(fullName))
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");

            bool selectListRestored = false;

            if (selectList == null)
            {
                selectList = htmlHelper.GetSelectData3(shortName);
                selectListRestored = true;
            }

            object selectListModelState = allowMultiple ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));
            if (!selectListRestored && selectListModelState == null)
                selectListModelState = htmlHelper.ViewData.Eval(shortName);

            if (selectListModelState != null)
            {
                IEnumerable source = allowMultiple ? selectListModelState as IEnumerable : (IEnumerable)new object[] { selectListModelState };

                HashSet<string> set = new HashSet<string>(
                    source
                        .Cast<object>()
                        .Select<object, string>(x => Convert.ToString(x, CultureInfo.CurrentCulture)),
                    StringComparer.OrdinalIgnoreCase);

                List<SelectListItem3> list = new List<SelectListItem3>();
                foreach (SelectListItem3 item in selectList)
                {
                    SelectListItem3OptionGroup group = item as SelectListItem3OptionGroup;

                    if (group == null)
                    {
                        SelectListItem3Option option = item as SelectListItem3Option;
                        option.Selected = option.Value != null ? set.Contains(option.Value) : set.Contains(option.Text);
                        list.Add(option);
                    }
                    else
                    {
                        foreach (SelectListItem3 item2 in group.Items)
                        {
                            SelectListItem3Option option = item2 as SelectListItem3Option;
                            option.Selected = option.Value != null ? set.Contains(option.Value) : set.Contains(option.Text);
                        }

                        list.Add(group);
                    }
                }
                selectList = list;
            }
            StringBuilder builder = new StringBuilder();

            if (optionLabel != null)
            {
                SelectListItem3Option defaultItem = new SelectListItem3Option();
                defaultItem.Text = optionLabel;
                defaultItem.Value = string.Empty;
                defaultItem.Selected = false;
                builder.AppendLine(OptionToHtml(defaultItem));
            }

            foreach (SelectListItem3 item in selectList)
            {
                SelectListItem3OptionGroup group = item as SelectListItem3OptionGroup;
                SelectListItem3Option option = item as SelectListItem3Option;

                if (group == null && option != null)
                    builder.AppendLine(OptionToHtml(item as SelectListItem3Option));
                else if (group != null && option == null)
                    builder.AppendLine(OptionGroupToHtml(group));
                else
                    throw new InvalidOperationException("Incorrect data type.");
            }

            TagBuilder builderSelect = new TagBuilder("select");
            builderSelect.InnerHtml = builder.ToString();
            builderSelect.MergeAttributes<string, object>(htmlAttributes);
            builderSelect.MergeAttribute("name", fullName, true);
            builderSelect.GenerateId(fullName);

            if (allowMultiple)
                builderSelect.MergeAttribute("multiple", "multiple");

            ModelState state;
            if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out state) && (state.Errors.Count > 0))
                builderSelect.AddCssClass(HtmlHelper.ValidationInputCssClassName);

            return builderSelect.ToMvcHtmlString(TagRenderMode.Normal);
        }

        private static IEnumerable<SelectListItem3> GetSelectData3(this HtmlHelper htmlHelper, string name)
        {
            object data = null;
            if (htmlHelper.ViewData != null)
                data = htmlHelper.ViewData.Eval(name);

            if (data == null)
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, MvcResources.HtmlHelper_MissingSelectData, new object[] { name, "IEnumerable<SelectListItem3>" }));

            IEnumerable<SelectListItem3> enumerable = data as IEnumerable<SelectListItem3>;
            if (enumerable == null)
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, MvcResources.HtmlHelper_WrongSelectDataType, new object[] { name, data.GetType().FullName, "IEnumerable<SelectListItem3>" }));

            return enumerable;
        }

        private static string OptionGroupToHtml(SelectListItem3OptionGroup group)
        {
            TagBuilder builder = new TagBuilder("optgroup");
            builder.Attributes["label"] = HttpUtility.HtmlAttributeEncode(group.Text);

            if (group.Items != null && group.Items.Count() > 0)
            {
                StringBuilder items = new StringBuilder();
                foreach (SelectListItem3Option item in group.Items)
                    items.AppendLine(OptionToHtml(item));

                builder.InnerHtml = items.ToString();
            }

            return builder.ToString();
        }

        private static string OptionToHtml(SelectListItem3Option item)
        {
            TagBuilder builder = new TagBuilder("option");
            builder.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);
        }

        #region Common

        private static object GetModelStateValue(this HtmlHelper self, string key, Type destinationType)
        {
            ModelState state;
            if (self.ViewData.ModelState.TryGetValue(key, out state) && (state.Value != null))
                return state.Value.ConvertTo(destinationType, null);

            return null;
        }

        private static MvcHtmlString ToMvcHtmlString(this TagBuilder self, TagRenderMode renderMode)
        {
            return MvcHtmlString.Create(self.ToString(renderMode));
        }

        private static class MvcResources
        {
            private static CultureInfo resourceCulture;
            private static ResourceManager resourceMan;

            [EditorBrowsable(EditorBrowsableState.Advanced)]
            internal static CultureInfo Culture
            {
                get
                {
                    return resourceCulture;
                }
                set
                {
                    resourceCulture = value;
                }
            }

            [EditorBrowsable(EditorBrowsableState.Advanced)]
            internal static ResourceManager ResourceManager
            {
                get
                {
                    if (object.ReferenceEquals(resourceMan, null))
                    {
                        Assembly mvcAssembly = Assembly.Load("System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
                        ResourceManager manager = new ResourceManager("System.Web.Mvc.Resources.MvcResources", mvcAssembly);
                        resourceMan = manager;
                    }
                    return resourceMan;
                }
            }

            internal static string Common_NullOrEmpty
            {
                get
                {
                    return ResourceManager.GetString("Common_NullOrEmpty", resourceCulture);
                }
            }

            internal static string HtmlHelper_MissingSelectData
            {
                get
                {
                    return ResourceManager.GetString("HtmlHelper_MissingSelectData", resourceCulture);
                }
            }

            internal static string HtmlHelper_WrongSelectDataType
            {
                get
                {
                    return ResourceManager.GetString("HtmlHelper_WrongSelectDataType", resourceCulture);
                }
            }
        }

        #endregion
    }

    /// <summary>Represents the base select item.</summary>
    public abstract class SelectListItem3
    {
        /// <summary>Gets or sets the text of the item.</summary>
        /// <returns>The text.</returns>
        public string Text { get; set; }
    }

    /// <summary>Represents the select optgroup.</summary>
    public class SelectListItem3OptionGroup : SelectListItem3
    {
        /// <summary>Gets or sets items of the group.</summary>
        public IEnumerable<SelectListItem3Option> Items { get; set; }
    }

    /// <summary>Represents the select option.</summary>
    public class SelectListItem3Option : SelectListItem3
    {
        /// <summary>Gets or sets a value that indicates whether this instance is selected.</summary>
        /// <returns>true if the item is selected; otherwise, false.</returns>
        public bool Selected { get; set; }

        /// <summary>Gets or sets the value of the selected item.</summary>
        /// <returns>The value.</returns>
        public string Value { get; set; }
    }
}

See you later!


Shout it

kick it on DotNetKicks.com

2 comments:

  1. Thank you for your code.

    I think that line 415 is better to replace from:

    builder.Attributes["label"] = HttpUtility.HtmlEncode(group.Text);

    to

    builder.Attributes["label"] = HttpUtility.HtmlAttributeEncode(group.Text);

    Best
    Michal

    ReplyDelete