Fix bugs in sizing TableLayoutPanel (Xamarin bug 18638)
[mono.git] / mcs / class / System.Web.Mvc2 / System.Web.Mvc / Html / SelectExtensions.cs
1 /* ****************************************************************************\r
2  *\r
3  * Copyright (c) Microsoft Corporation. All rights reserved.\r
4  *\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
8  *\r
9  * You must not remove this notice, or any other, from this software.\r
10  *\r
11  * ***************************************************************************/\r
12 \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
18     using System.Linq;\r
19     using System.Linq.Expressions;\r
20     using System.Text;\r
21     using System.Web;\r
22     using System.Web.Mvc.Resources;\r
23     using System.Web.Routing;\r
24 \r
25     public static class SelectExtensions {\r
26 \r
27         // DropDownList\r
28 \r
29         public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name) {\r
30             return DropDownList(htmlHelper, name, null /* selectList */, null /* optionLabel */, null /* htmlAttributes */);\r
31         }\r
32 \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
35         }\r
36 \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
39         }\r
40 \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
43         }\r
44 \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
47         }\r
48 \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
51         }\r
52 \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
55         }\r
56 \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
59         }\r
60 \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
64         }\r
65 \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
69         }\r
70 \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
74         }\r
75 \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
79         }\r
80 \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
84         }\r
85 \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
91             }\r
92 \r
93             return DropDownListHelper(htmlHelper, ExpressionHelper.GetExpressionText(expression), selectList, optionLabel, htmlAttributes);\r
94         }\r
95 \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
98         }\r
99 \r
100         // ListBox\r
101 \r
102         public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name) {\r
103             return ListBox(htmlHelper, name, null /* selectList */, null /* htmlAttributes */);\r
104         }\r
105 \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
108         }\r
109 \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
112         }\r
113 \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
116         }\r
117 \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
121         }\r
122 \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
126         }\r
127 \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
133             }\r
134 \r
135             return ListBoxHelper(htmlHelper,\r
136                                  ExpressionHelper.GetExpressionText(expression),\r
137                                  selectList,\r
138                                  htmlAttributes);\r
139         }\r
140 \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
143         }\r
144 \r
145         // Helper methods\r
146 \r
147         private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name) {\r
148             object o = null;\r
149             if (htmlHelper.ViewData != null) {\r
150                 o = htmlHelper.ViewData.Eval(name);\r
151             }\r
152             if (o == null) {\r
153                 throw new InvalidOperationException(\r
154                     String.Format(\r
155                         CultureInfo.CurrentUICulture,\r
156                         MvcResources.HtmlHelper_MissingSelectData,\r
157                         name,\r
158                         "IEnumerable<SelectListItem>"));\r
159             }\r
160             IEnumerable<SelectListItem> selectList = o as IEnumerable<SelectListItem>;\r
161             if (selectList == null) {\r
162                 throw new InvalidOperationException(\r
163                     String.Format(\r
164                         CultureInfo.CurrentUICulture,\r
165                         MvcResources.HtmlHelper_WrongSelectDataType,\r
166                         name,\r
167                         o.GetType().FullName,\r
168                         "IEnumerable<SelectListItem>"));\r
169             }\r
170             return selectList;\r
171         }\r
172 \r
173         internal static string ListItemToOption(SelectListItem item) {\r
174             TagBuilder builder = new TagBuilder("option") {\r
175                 InnerHtml = HttpUtility.HtmlEncode(item.Text)\r
176             };\r
177             if (item.Value != null) {\r
178                 builder.Attributes["value"] = item.Value;\r
179             }\r
180             if (item.Selected) {\r
181                 builder.Attributes["selected"] = "selected";\r
182             }\r
183             return builder.ToString(TagRenderMode.Normal);\r
184         }\r
185 \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
190             }\r
191 \r
192             bool usedViewData = false;\r
193 \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
198             }\r
199 \r
200             object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(name, typeof(string[])) : htmlHelper.GetModelStateValue(name, typeof(string));\r
201 \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
207                 }\r
208             }\r
209 \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
215 \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
219                 }\r
220                 selectList = newSelectList;\r
221             }\r
222 \r
223             // Convert each ListItem to an <option> tag\r
224             StringBuilder listItemBuilder = new StringBuilder();\r
225 \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
229             }\r
230 \r
231             foreach (SelectListItem item in selectList) {\r
232                 listItemBuilder.AppendLine(ListItemToOption(item));\r
233             }\r
234 \r
235             TagBuilder tagBuilder = new TagBuilder("select") {\r
236                 InnerHtml = listItemBuilder.ToString()\r
237             };\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
243             }\r
244 \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
250                 }\r
251             }\r
252 \r
253             return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);\r
254         }\r
255     }\r
256 }\r