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
15 using System.Collections.Generic;
\r
16 using System.Data.Linq;
\r
17 using System.Diagnostics.CodeAnalysis;
\r
18 using System.Globalization;
\r
19 using System.Linq.Expressions;
\r
21 using System.Web.Mvc.Resources;
\r
22 using System.Web.Routing;
\r
24 public static class InputExtensions {
\r
27 public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name) {
\r
28 return CheckBox(htmlHelper, name, (object)null /* htmlAttributes */);
\r
31 public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool isChecked) {
\r
32 return CheckBox(htmlHelper, name, isChecked, (object)null /* htmlAttributes */);
\r
35 public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool isChecked, object htmlAttributes) {
\r
36 return CheckBox(htmlHelper, name, isChecked, new RouteValueDictionary(htmlAttributes));
\r
39 public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, object htmlAttributes) {
\r
40 return CheckBox(htmlHelper, name, new RouteValueDictionary(htmlAttributes));
\r
43 public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes) {
\r
44 return CheckBoxHelper(htmlHelper, name, null /* isChecked */, htmlAttributes);
\r
47 public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool isChecked, IDictionary<string, object> htmlAttributes) {
\r
48 return CheckBoxHelper(htmlHelper, name, isChecked, htmlAttributes);
\r
51 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
52 public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression) {
\r
53 return CheckBoxFor(htmlHelper, expression, null /* htmlAttributes */);
\r
56 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
57 public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, object htmlAttributes) {
\r
58 return CheckBoxFor(htmlHelper, expression, new RouteValueDictionary(htmlAttributes));
\r
61 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
62 public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, IDictionary<string, object> htmlAttributes) {
\r
63 if (expression == null) {
\r
64 throw new ArgumentNullException("expression");
\r
67 ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
\r
68 bool? isChecked = null;
\r
69 if (metadata.Model != null) {
\r
71 if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked)) {
\r
72 isChecked = modelChecked;
\r
76 return CheckBoxHelper(htmlHelper, ExpressionHelper.GetExpressionText(expression), isChecked, htmlAttributes);
\r
79 private static MvcHtmlString CheckBoxHelper(HtmlHelper htmlHelper, string name, bool? isChecked, IDictionary<string, object> htmlAttributes) {
\r
80 RouteValueDictionary attributes =
\r
81 htmlAttributes == null ? new RouteValueDictionary()
\r
82 : new RouteValueDictionary(htmlAttributes);
\r
84 bool explicitValue = isChecked.HasValue;
\r
85 if (explicitValue) {
\r
86 attributes.Remove("checked"); // Explicit value must override dictionary
\r
89 return InputHelper(htmlHelper, InputType.CheckBox, name, "true", !explicitValue /* useViewData */, isChecked ?? false, true /* setId */, false /* isExplicitValue */, attributes);
\r
94 public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name) {
\r
95 return Hidden(htmlHelper, name, null /* value */, null /* htmlAttributes */);
\r
98 public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name, object value) {
\r
99 return Hidden(htmlHelper, name, value, null /* hmtlAttributes */);
\r
102 public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes) {
\r
103 return Hidden(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));
\r
106 public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
\r
107 return HiddenHelper(htmlHelper,
\r
109 value == null /* useViewData */,
\r
114 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
115 public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
\r
116 return HiddenFor(htmlHelper, expression, (IDictionary<string, object>)null);
\r
119 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
120 public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) {
\r
121 return HiddenFor(htmlHelper, expression, new RouteValueDictionary(htmlAttributes));
\r
124 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
125 public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) {
\r
126 return HiddenHelper(htmlHelper,
\r
127 ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model,
\r
129 ExpressionHelper.GetExpressionText(expression),
\r
133 private static MvcHtmlString HiddenHelper(HtmlHelper htmlHelper, object value, bool useViewData, string expression, IDictionary<string, object> htmlAttributes) {
\r
134 Binary binaryValue = value as Binary;
\r
135 if (binaryValue != null) {
\r
136 value = binaryValue.ToArray();
\r
139 byte[] byteArrayValue = value as byte[];
\r
140 if (byteArrayValue != null) {
\r
141 value = Convert.ToBase64String(byteArrayValue);
\r
144 return InputHelper(htmlHelper, InputType.Hidden, expression, value, useViewData, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
\r
149 public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name) {
\r
150 return Password(htmlHelper, name, null /* value */);
\r
153 public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name, object value) {
\r
154 return Password(htmlHelper, name, value, null /* htmlAttributes */);
\r
157 public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes) {
\r
158 return Password(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));
\r
161 public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
\r
162 return PasswordHelper(htmlHelper, name, value, htmlAttributes);
\r
165 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
166 public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
\r
167 return PasswordFor(htmlHelper, expression, null /* htmlAttributes */);
\r
170 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
171 public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) {
\r
172 return PasswordFor(htmlHelper, expression, new RouteValueDictionary(htmlAttributes));
\r
175 [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")]
\r
176 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
177 public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) {
\r
178 if (expression == null) {
\r
179 throw new ArgumentNullException("expression");
\r
182 return PasswordHelper(htmlHelper,
\r
183 ExpressionHelper.GetExpressionText(expression),
\r
188 private static MvcHtmlString PasswordHelper(HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
\r
189 return InputHelper(htmlHelper, InputType.Password, name, value, false /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
\r
194 public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value) {
\r
195 return RadioButton(htmlHelper, name, value, (object)null /* htmlAttributes */);
\r
198 public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes) {
\r
199 return RadioButton(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));
\r
202 public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
\r
203 // Determine whether or not to render the checked attribute based on the contents of ViewData.
\r
204 string valueString = Convert.ToString(value, CultureInfo.CurrentCulture);
\r
205 bool isChecked = (!String.IsNullOrEmpty(name)) && (String.Equals(htmlHelper.EvalString(name), valueString, StringComparison.OrdinalIgnoreCase));
\r
206 // checked attributes is implicit, so we need to ensure that the dictionary takes precedence.
\r
207 RouteValueDictionary attributes = htmlAttributes == null ? new RouteValueDictionary() : new RouteValueDictionary(htmlAttributes);
\r
208 if (attributes.ContainsKey("checked")) {
\r
209 return InputHelper(htmlHelper, InputType.Radio, name, value, false, false, true, true /* isExplicitValue */, attributes);
\r
212 return RadioButton(htmlHelper, name, value, isChecked, htmlAttributes);
\r
215 public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked) {
\r
216 return RadioButton(htmlHelper, name, value, isChecked, (object)null /* htmlAttributes */);
\r
219 public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, object htmlAttributes) {
\r
220 return RadioButton(htmlHelper, name, value, isChecked, new RouteValueDictionary(htmlAttributes));
\r
223 public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, IDictionary<string, object> htmlAttributes) {
\r
224 if (value == null) {
\r
225 throw new ArgumentNullException("value");
\r
227 // checked attribute is an explicit parameter so it takes precedence.
\r
228 RouteValueDictionary attributes = htmlAttributes == null ? new RouteValueDictionary() : new RouteValueDictionary(htmlAttributes);
\r
229 attributes.Remove("checked");
\r
230 return InputHelper(htmlHelper, InputType.Radio, name, value, false, isChecked, true, true /* isExplicitValue */, attributes);
\r
233 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
234 public static MvcHtmlString RadioButtonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object value) {
\r
235 return RadioButtonFor(htmlHelper, expression, value, null /* htmlAttributes */);
\r
238 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
239 public static MvcHtmlString RadioButtonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object value, object htmlAttributes) {
\r
240 return RadioButtonFor(htmlHelper, expression, value, new RouteValueDictionary(htmlAttributes));
\r
243 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
244 public static MvcHtmlString RadioButtonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object value, IDictionary<string, object> htmlAttributes) {
\r
245 return RadioButtonHelper(htmlHelper,
\r
246 ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model,
\r
247 ExpressionHelper.GetExpressionText(expression),
\r
249 null /* isChecked */,
\r
253 private static MvcHtmlString RadioButtonHelper(HtmlHelper htmlHelper, object model, string name, object value, bool? isChecked, IDictionary<string, object> htmlAttributes) {
\r
254 if (value == null) {
\r
255 throw new ArgumentNullException("value");
\r
258 RouteValueDictionary attributes =
\r
259 htmlAttributes == null ? new RouteValueDictionary()
\r
260 : new RouteValueDictionary(htmlAttributes);
\r
262 bool explicitValue = isChecked.HasValue;
\r
263 if (explicitValue) {
\r
264 attributes.Remove("checked"); // Explicit value must override dictionary
\r
267 string valueString = Convert.ToString(value, CultureInfo.CurrentCulture);
\r
268 isChecked = model != null &&
\r
269 !String.IsNullOrEmpty(name) &&
\r
270 String.Equals(model.ToString(), valueString, StringComparison.OrdinalIgnoreCase);
\r
273 return InputHelper(htmlHelper, InputType.Radio, name, value, false /* useViewData */, isChecked ?? false, true /* setId */, true /* isExplicitValue */, attributes);
\r
278 public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name) {
\r
279 return TextBox(htmlHelper, name, null /* value */);
\r
282 public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value) {
\r
283 return TextBox(htmlHelper, name, value, (object)null /* htmlAttributes */);
\r
286 public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes) {
\r
287 return TextBox(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));
\r
290 public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
\r
291 return InputHelper(htmlHelper, InputType.Text, name, value, (value == null) /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
\r
294 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
295 public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
\r
296 return htmlHelper.TextBoxFor(expression, (IDictionary<string, object>)null);
\r
299 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
300 public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) {
\r
301 return htmlHelper.TextBoxFor(expression, new RouteValueDictionary(htmlAttributes));
\r
304 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
\r
305 public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) {
\r
306 return TextBoxHelper(htmlHelper,
\r
307 ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model,
\r
308 ExpressionHelper.GetExpressionText(expression),
\r
312 private static MvcHtmlString TextBoxHelper(this HtmlHelper htmlHelper, object model, string expression, IDictionary<string, object> htmlAttributes) {
\r
313 return InputHelper(htmlHelper, InputType.Text, expression, model, false /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
\r
318 private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, IDictionary<string, object> htmlAttributes) {
\r
319 name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
\r
320 if (String.IsNullOrEmpty(name)) {
\r
321 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
\r
324 TagBuilder tagBuilder = new TagBuilder("input");
\r
325 tagBuilder.MergeAttributes(htmlAttributes);
\r
326 tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
\r
327 tagBuilder.MergeAttribute("name", name, true);
\r
329 string valueParameter = Convert.ToString(value, CultureInfo.CurrentCulture);
\r
330 bool usedModelState = false;
\r
332 switch (inputType) {
\r
333 case InputType.CheckBox:
\r
334 bool? modelStateWasChecked = htmlHelper.GetModelStateValue(name, typeof(bool)) as bool?;
\r
335 if (modelStateWasChecked.HasValue) {
\r
336 isChecked = modelStateWasChecked.Value;
\r
337 usedModelState = true;
\r
339 goto case InputType.Radio;
\r
340 case InputType.Radio:
\r
341 if (!usedModelState) {
\r
342 string modelStateValue = htmlHelper.GetModelStateValue(name, typeof(string)) as string;
\r
343 if (modelStateValue != null) {
\r
344 isChecked = String.Equals(modelStateValue, valueParameter, StringComparison.Ordinal);
\r
345 usedModelState = true;
\r
348 if (!usedModelState && useViewData) {
\r
349 isChecked = htmlHelper.EvalBoolean(name);
\r
352 tagBuilder.MergeAttribute("checked", "checked");
\r
354 tagBuilder.MergeAttribute("value", valueParameter, isExplicitValue);
\r
356 case InputType.Password:
\r
357 if (value != null) {
\r
358 tagBuilder.MergeAttribute("value", valueParameter, isExplicitValue);
\r
362 string attemptedValue = (string)htmlHelper.GetModelStateValue(name, typeof(string));
\r
363 tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(name) : valueParameter), isExplicitValue);
\r
368 tagBuilder.GenerateId(name);
\r
371 // If there are any errors for a named field, we add the css attribute.
\r
372 ModelState modelState;
\r
373 if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState)) {
\r
374 if (modelState.Errors.Count > 0) {
\r
375 tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
\r
379 if (inputType == InputType.CheckBox) {
\r
380 // Render an additional <input type="hidden".../> for checkboxes. This
\r
381 // addresses scenarios where unchecked checkboxes are not sent in the request.
\r
382 // Sending a hidden input makes it possible to know that the checkbox was present
\r
383 // on the page when the request was submitted.
\r
384 StringBuilder inputItemBuilder = new StringBuilder();
\r
385 inputItemBuilder.Append(tagBuilder.ToString(TagRenderMode.SelfClosing));
\r
387 TagBuilder hiddenInput = new TagBuilder("input");
\r
388 hiddenInput.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
\r
389 hiddenInput.MergeAttribute("name", name);
\r
390 hiddenInput.MergeAttribute("value", "false");
\r
391 inputItemBuilder.Append(hiddenInput.ToString(TagRenderMode.SelfClosing));
\r
392 return MvcHtmlString.Create(inputItemBuilder.ToString());
\r
395 return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
\r