1 /* ****************************************************************************
\r
3 * Copyright (c) Microsoft Corporation. All rights reserved.
\r
5 * This software is subject to the Microsoft Public License (Ms-PL).
\r
6 * A copy of the license can be found in the license.htm file included
\r
7 * in this distribution.
\r
9 * You must not remove this notice, or any other, from this software.
\r
11 * ***************************************************************************/
\r
13 namespace System.Web.Mvc.Html {
\r
14 using System.Collections;
\r
15 using System.Collections.Generic;
\r
16 using System.Diagnostics.CodeAnalysis;
\r
17 using System.Globalization;
\r
19 using System.Linq.Expressions;
\r
22 using System.Web.Mvc.Resources;
\r
23 using System.Web.Routing;
\r
25 public static class SelectExtensions {
\r
29 public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name) {
\r
30 return DropDownList(htmlHelper, name, null /* selectList */, null /* optionLabel */, null /* htmlAttributes */);
\r
33 public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, string optionLabel) {
\r
34 return DropDownList(htmlHelper, name, null /* selectList */, optionLabel, null /* htmlAttributes */);
\r
37 public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList) {
\r
38 return DropDownList(htmlHelper, name, selectList, null /* optionLabel */, null /* htmlAttributes */);
\r
41 public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, object htmlAttributes) {
\r
42 return DropDownList(htmlHelper, name, selectList, null /* optionLabel */, new RouteValueDictionary(htmlAttributes));
\r
45 public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
\r
46 return DropDownList(htmlHelper, name, selectList, null /* optionLabel */, htmlAttributes);
\r
49 public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, string optionLabel) {
\r
50 return DropDownList(htmlHelper, name, selectList, optionLabel, null /* htmlAttributes */);
\r
53 public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, string optionLabel, object htmlAttributes) {
\r
54 return DropDownList(htmlHelper, name, selectList, optionLabel, new RouteValueDictionary(htmlAttributes));
\r
57 public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) {
\r
58 return DropDownListHelper(htmlHelper, name, selectList, optionLabel, htmlAttributes);
\r
61 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
62 public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList) {
\r
63 return DropDownListFor(htmlHelper, expression, selectList, null /* optionLabel */, null /* htmlAttributes */);
\r
66 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
67 public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, object htmlAttributes) {
\r
68 return DropDownListFor(htmlHelper, expression, selectList, null /* optionLabel */, new RouteValueDictionary(htmlAttributes));
\r
71 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
72 public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
\r
73 return DropDownListFor(htmlHelper, expression, selectList, null /* optionLabel */, htmlAttributes);
\r
76 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
77 public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel) {
\r
78 return DropDownListFor(htmlHelper, expression, selectList, optionLabel, null /* htmlAttributes */);
\r
81 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
82 public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel, object htmlAttributes) {
\r
83 return DropDownListFor(htmlHelper, expression, selectList, optionLabel, new RouteValueDictionary(htmlAttributes));
\r
86 [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")]
\r
87 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
88 public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) {
\r
89 if (expression == null) {
\r
90 throw new ArgumentNullException("expression");
\r
93 return DropDownListHelper(htmlHelper, ExpressionHelper.GetExpressionText(expression), selectList, optionLabel, htmlAttributes);
\r
96 private static MvcHtmlString DropDownListHelper(HtmlHelper htmlHelper, string expression, IEnumerable<SelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) {
\r
97 return SelectInternal(htmlHelper, optionLabel, expression, selectList, false /* allowMultiple */, htmlAttributes);
\r
102 public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name) {
\r
103 return ListBox(htmlHelper, name, null /* selectList */, null /* htmlAttributes */);
\r
106 public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList) {
\r
107 return ListBox(htmlHelper, name, selectList, (IDictionary<string, object>)null);
\r
110 public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, object htmlAttributes) {
\r
111 return ListBox(htmlHelper, name, selectList, new RouteValueDictionary(htmlAttributes));
\r
114 public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
\r
115 return ListBoxHelper(htmlHelper, name, selectList, htmlAttributes);
\r
118 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
119 public static MvcHtmlString ListBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList) {
\r
120 return ListBoxFor(htmlHelper, expression, selectList, null /* htmlAttributes */);
\r
123 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
124 public static MvcHtmlString ListBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, object htmlAttributes) {
\r
125 return ListBoxFor(htmlHelper, expression, selectList, new RouteValueDictionary(htmlAttributes));
\r
128 [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")]
\r
129 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
130 public static MvcHtmlString ListBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
\r
131 if (expression == null) {
\r
132 throw new ArgumentNullException("expression");
\r
135 return ListBoxHelper(htmlHelper,
\r
136 ExpressionHelper.GetExpressionText(expression),
\r
141 private static MvcHtmlString ListBoxHelper(HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
\r
142 return SelectInternal(htmlHelper, null /* optionLabel */, name, selectList, true /* allowMultiple */, htmlAttributes);
\r
147 private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name) {
\r
149 if (htmlHelper.ViewData != null) {
\r
150 o = htmlHelper.ViewData.Eval(name);
\r
153 throw new InvalidOperationException(
\r
155 CultureInfo.CurrentUICulture,
\r
156 MvcResources.HtmlHelper_MissingSelectData,
\r
158 "IEnumerable<SelectListItem>"));
\r
160 IEnumerable<SelectListItem> selectList = o as IEnumerable<SelectListItem>;
\r
161 if (selectList == null) {
\r
162 throw new InvalidOperationException(
\r
164 CultureInfo.CurrentUICulture,
\r
165 MvcResources.HtmlHelper_WrongSelectDataType,
\r
167 o.GetType().FullName,
\r
168 "IEnumerable<SelectListItem>"));
\r
173 internal static string ListItemToOption(SelectListItem item) {
\r
174 TagBuilder builder = new TagBuilder("option") {
\r
175 InnerHtml = HttpUtility.HtmlEncode(item.Text)
\r
177 if (item.Value != null) {
\r
178 builder.Attributes["value"] = item.Value;
\r
180 if (item.Selected) {
\r
181 builder.Attributes["selected"] = "selected";
\r
183 return builder.ToString(TagRenderMode.Normal);
\r
186 private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes) {
\r
187 name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
\r
188 if (String.IsNullOrEmpty(name)) {
\r
189 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
\r
192 bool usedViewData = false;
\r
194 // If we got a null selectList, try to use ViewData to get the list of items.
\r
195 if (selectList == null) {
\r
196 selectList = htmlHelper.GetSelectData(name);
\r
197 usedViewData = true;
\r
200 object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(name, typeof(string[])) : htmlHelper.GetModelStateValue(name, typeof(string));
\r
202 // If we haven't already used ViewData to get the entire list of items then we need to
\r
203 // use the ViewData-supplied value before using the parameter-supplied value.
\r
204 if (!usedViewData) {
\r
205 if (defaultValue == null) {
\r
206 defaultValue = htmlHelper.ViewData.Eval(name);
\r
210 if (defaultValue != null) {
\r
211 IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
\r
212 IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture);
\r
213 HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
\r
214 List<SelectListItem> newSelectList = new List<SelectListItem>();
\r
216 foreach (SelectListItem item in selectList) {
\r
217 item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
\r
218 newSelectList.Add(item);
\r
220 selectList = newSelectList;
\r
223 // Convert each ListItem to an <option> tag
\r
224 StringBuilder listItemBuilder = new StringBuilder();
\r
226 // Make optionLabel the first item that gets rendered.
\r
227 if (optionLabel != null) {
\r
228 listItemBuilder.AppendLine(ListItemToOption(new SelectListItem() { Text = optionLabel, Value = String.Empty, Selected = false }));
\r
231 foreach (SelectListItem item in selectList) {
\r
232 listItemBuilder.AppendLine(ListItemToOption(item));
\r
235 TagBuilder tagBuilder = new TagBuilder("select") {
\r
236 InnerHtml = listItemBuilder.ToString()
\r
238 tagBuilder.MergeAttributes(htmlAttributes);
\r
239 tagBuilder.MergeAttribute("name", name, true /* replaceExisting */);
\r
240 tagBuilder.GenerateId(name);
\r
241 if (allowMultiple) {
\r
242 tagBuilder.MergeAttribute("multiple", "multiple");
\r
245 // If there are any errors for a named field, we add the css attribute.
\r
246 ModelState modelState;
\r
247 if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState)) {
\r
248 if (modelState.Errors.Count > 0) {
\r
249 tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
\r
253 return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
\r