1
namespace System
.Web
.Mvc
.Html
{
3 using System
.Collections
.Generic
;
4 using System
.Data
.Linq
;
5 using System
.Diagnostics
.CodeAnalysis
;
6 using System
.Globalization
;
7 using System
.Linq
.Expressions
;
9 using System
.Web
.Mvc
.Resources
;
10 using System
.Web
.Routing
;
12 public static class InputExtensions
{
15 public static MvcHtmlString
CheckBox(this HtmlHelper htmlHelper
, string name
) {
16 return CheckBox(htmlHelper
, name
, (object)null /* htmlAttributes */);
19 public static MvcHtmlString
CheckBox(this HtmlHelper htmlHelper
, string name
, bool isChecked
) {
20 return CheckBox(htmlHelper
, name
, isChecked
, (object)null /* htmlAttributes */);
23 public static MvcHtmlString
CheckBox(this HtmlHelper htmlHelper
, string name
, bool isChecked
, object htmlAttributes
) {
24 return CheckBox(htmlHelper
, name
, isChecked
, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
27 public static MvcHtmlString
CheckBox(this HtmlHelper htmlHelper
, string name
, object htmlAttributes
) {
28 return CheckBox(htmlHelper
, name
, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
31 public static MvcHtmlString
CheckBox(this HtmlHelper htmlHelper
, string name
, IDictionary
<string, object> htmlAttributes
) {
32 return CheckBoxHelper(htmlHelper
, null, name
, null /* isChecked */, htmlAttributes
);
35 public static MvcHtmlString
CheckBox(this HtmlHelper htmlHelper
, string name
, bool isChecked
, IDictionary
<string, object> htmlAttributes
) {
36 return CheckBoxHelper(htmlHelper
, null, name
, isChecked
, htmlAttributes
);
39 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
40 public static MvcHtmlString CheckBoxFor
<TModel
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, bool>> expression
) {
41 return CheckBoxFor(htmlHelper
, expression
, null /* htmlAttributes */);
44 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
45 public static MvcHtmlString CheckBoxFor
<TModel
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, bool>> expression
, object htmlAttributes
) {
46 return CheckBoxFor(htmlHelper
, expression
, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
49 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
50 public static MvcHtmlString CheckBoxFor
<TModel
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, bool>> expression
, IDictionary
<string, object> htmlAttributes
) {
51 if (expression
== null) {
52 throw new ArgumentNullException("expression");
55 ModelMetadata metadata
= ModelMetadata
.FromLambdaExpression(expression
, htmlHelper
.ViewData
);
56 bool? isChecked
= null;
57 if (metadata
.Model
!= null) {
59 if (Boolean
.TryParse(metadata
.Model
.ToString(), out modelChecked
)) {
60 isChecked
= modelChecked
;
64 return CheckBoxHelper(htmlHelper
, metadata
, ExpressionHelper
.GetExpressionText(expression
), isChecked
, htmlAttributes
);
67 private static MvcHtmlString
CheckBoxHelper(HtmlHelper htmlHelper
, ModelMetadata metadata
, string name
, bool? isChecked
, IDictionary
<string, object> htmlAttributes
) {
68 RouteValueDictionary attributes
= ToRouteValueDictionary(htmlAttributes
);
70 bool explicitValue
= isChecked
.HasValue
;
72 attributes
.Remove("checked"); // Explicit value must override dictionary
75 return InputHelper(htmlHelper
, InputType
.CheckBox
, metadata
, name
, "true", !explicitValue
/* useViewData */, isChecked
?? false, true /* setId */, false /* isExplicitValue */, attributes
);
80 public static MvcHtmlString
Hidden(this HtmlHelper htmlHelper
, string name
) {
81 return Hidden(htmlHelper
, name
, null /* value */, null /* htmlAttributes */);
84 public static MvcHtmlString
Hidden(this HtmlHelper htmlHelper
, string name
, object value) {
85 return Hidden(htmlHelper
, name
, value, null /* hmtlAttributes */);
88 public static MvcHtmlString
Hidden(this HtmlHelper htmlHelper
, string name
, object value, object htmlAttributes
) {
89 return Hidden(htmlHelper
, name
, value, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
92 public static MvcHtmlString
Hidden(this HtmlHelper htmlHelper
, string name
, object value, IDictionary
<string, object> htmlAttributes
) {
93 return HiddenHelper(htmlHelper
,
96 value == null /* useViewData */,
101 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
102 public static MvcHtmlString HiddenFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
) {
103 return HiddenFor(htmlHelper
, expression
, (IDictionary
<string, object>)null);
106 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
107 public static MvcHtmlString HiddenFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
, object htmlAttributes
) {
108 return HiddenFor(htmlHelper
, expression
, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
111 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
112 public static MvcHtmlString HiddenFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
, IDictionary
<string, object> htmlAttributes
) {
113 ModelMetadata metadata
= ModelMetadata
.FromLambdaExpression(expression
, htmlHelper
.ViewData
);
114 return HiddenHelper(htmlHelper
,
118 ExpressionHelper
.GetExpressionText(expression
),
122 private static MvcHtmlString
HiddenHelper(HtmlHelper htmlHelper
, ModelMetadata metadata
, object value, bool useViewData
, string expression
, IDictionary
<string, object> htmlAttributes
) {
123 Binary binaryValue
= value as Binary
;
124 if (binaryValue
!= null) {
125 value = binaryValue
.ToArray();
128 byte[] byteArrayValue
= value as byte[];
129 if (byteArrayValue
!= null) {
130 value = Convert
.ToBase64String(byteArrayValue
);
133 return InputHelper(htmlHelper
, InputType
.Hidden
, metadata
, expression
, value, useViewData
, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes
);
138 public static MvcHtmlString
Password(this HtmlHelper htmlHelper
, string name
) {
139 return Password(htmlHelper
, name
, null /* value */);
142 public static MvcHtmlString
Password(this HtmlHelper htmlHelper
, string name
, object value) {
143 return Password(htmlHelper
, name
, value, null /* htmlAttributes */);
146 public static MvcHtmlString
Password(this HtmlHelper htmlHelper
, string name
, object value, object htmlAttributes
) {
147 return Password(htmlHelper
, name
, value, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
150 public static MvcHtmlString
Password(this HtmlHelper htmlHelper
, string name
, object value, IDictionary
<string, object> htmlAttributes
) {
151 return PasswordHelper(htmlHelper
, null /* metadata */, name
, value, htmlAttributes
);
154 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
155 public static MvcHtmlString PasswordFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
) {
156 return PasswordFor(htmlHelper
, expression
, null /* htmlAttributes */);
159 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
160 public static MvcHtmlString PasswordFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
, object htmlAttributes
) {
161 return PasswordFor(htmlHelper
, expression
, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
164 [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification
= "Users cannot use anonymous methods with the LambdaExpression type")]
165 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
166 public static MvcHtmlString PasswordFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
, IDictionary
<string, object> htmlAttributes
) {
167 if (expression
== null) {
168 throw new ArgumentNullException("expression");
171 return PasswordHelper(htmlHelper
,
172 ModelMetadata
.FromLambdaExpression(expression
, htmlHelper
.ViewData
),
173 ExpressionHelper
.GetExpressionText(expression
),
178 private static MvcHtmlString
PasswordHelper(HtmlHelper htmlHelper
, ModelMetadata metadata
, string name
, object value, IDictionary
<string, object> htmlAttributes
) {
179 return InputHelper(htmlHelper
, InputType
.Password
, metadata
, name
, value, false /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes
);
184 public static MvcHtmlString
RadioButton(this HtmlHelper htmlHelper
, string name
, object value) {
185 return RadioButton(htmlHelper
, name
, value, (object)null /* htmlAttributes */);
188 public static MvcHtmlString
RadioButton(this HtmlHelper htmlHelper
, string name
, object value, object htmlAttributes
) {
189 return RadioButton(htmlHelper
, name
, value, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
192 public static MvcHtmlString
RadioButton(this HtmlHelper htmlHelper
, string name
, object value, IDictionary
<string, object> htmlAttributes
) {
193 // Determine whether or not to render the checked attribute based on the contents of ViewData.
194 string valueString
= Convert
.ToString(value, CultureInfo
.CurrentCulture
);
195 bool isChecked
= (!String
.IsNullOrEmpty(name
)) && (String
.Equals(htmlHelper
.EvalString(name
), valueString
, StringComparison
.OrdinalIgnoreCase
));
196 // checked attributes is implicit, so we need to ensure that the dictionary takes precedence.
197 RouteValueDictionary attributes
= ToRouteValueDictionary(htmlAttributes
);
198 if (attributes
.ContainsKey("checked")) {
199 return InputHelper(htmlHelper
, InputType
.Radio
, null, name
, value, false, false, true, true /* isExplicitValue */, attributes
);
202 return RadioButton(htmlHelper
, name
, value, isChecked
, htmlAttributes
);
205 public static MvcHtmlString
RadioButton(this HtmlHelper htmlHelper
, string name
, object value, bool isChecked
) {
206 return RadioButton(htmlHelper
, name
, value, isChecked
, (object)null /* htmlAttributes */);
209 public static MvcHtmlString
RadioButton(this HtmlHelper htmlHelper
, string name
, object value, bool isChecked
, object htmlAttributes
) {
210 return RadioButton(htmlHelper
, name
, value, isChecked
, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
213 public static MvcHtmlString
RadioButton(this HtmlHelper htmlHelper
, string name
, object value, bool isChecked
, IDictionary
<string, object> htmlAttributes
) {
215 throw new ArgumentNullException("value");
217 // checked attribute is an explicit parameter so it takes precedence.
218 RouteValueDictionary attributes
= ToRouteValueDictionary(htmlAttributes
);
219 attributes
.Remove("checked");
220 return InputHelper(htmlHelper
, InputType
.Radio
, null, name
, value, false, isChecked
, true, true /* isExplicitValue */, attributes
);
223 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
224 public static MvcHtmlString RadioButtonFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
, object value) {
225 return RadioButtonFor(htmlHelper
, expression
, value, null /* htmlAttributes */);
228 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
229 public static MvcHtmlString RadioButtonFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
, object value, object htmlAttributes
) {
230 return RadioButtonFor(htmlHelper
, expression
, value, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
233 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
234 public static MvcHtmlString RadioButtonFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
, object value, IDictionary
<string, object> htmlAttributes
) {
235 ModelMetadata metadata
= ModelMetadata
.FromLambdaExpression(expression
, htmlHelper
.ViewData
);
236 return RadioButtonHelper(htmlHelper
,
239 ExpressionHelper
.GetExpressionText(expression
),
241 null /* isChecked */,
245 private static MvcHtmlString
RadioButtonHelper(HtmlHelper htmlHelper
, ModelMetadata metadata
, object model
, string name
, object value, bool? isChecked
, IDictionary
<string, object> htmlAttributes
) {
247 throw new ArgumentNullException("value");
250 RouteValueDictionary attributes
= ToRouteValueDictionary(htmlAttributes
);
252 bool explicitValue
= isChecked
.HasValue
;
254 attributes
.Remove("checked"); // Explicit value must override dictionary
257 string valueString
= Convert
.ToString(value, CultureInfo
.CurrentCulture
);
258 isChecked
= model
!= null &&
259 !String
.IsNullOrEmpty(name
) &&
260 String
.Equals(model
.ToString(), valueString
, StringComparison
.OrdinalIgnoreCase
);
263 return InputHelper(htmlHelper
, InputType
.Radio
, metadata
, name
, value, false /* useViewData */, isChecked
?? false, true /* setId */, true /* isExplicitValue */, attributes
);
268 public static MvcHtmlString
TextBox(this HtmlHelper htmlHelper
, string name
) {
269 return TextBox(htmlHelper
, name
, null /* value */);
272 public static MvcHtmlString
TextBox(this HtmlHelper htmlHelper
, string name
, object value) {
273 return TextBox(htmlHelper
, name
, value, (object)null /* htmlAttributes */);
276 public static MvcHtmlString
TextBox(this HtmlHelper htmlHelper
, string name
, object value, object htmlAttributes
) {
277 return TextBox(htmlHelper
, name
, value, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
280 public static MvcHtmlString
TextBox(this HtmlHelper htmlHelper
, string name
, object value, IDictionary
<string, object> htmlAttributes
) {
281 return InputHelper(htmlHelper
, InputType
.Text
, null, name
, value, (value == null) /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes
);
284 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
285 public static MvcHtmlString TextBoxFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
) {
286 return htmlHelper
.TextBoxFor(expression
, (IDictionary
<string, object>)null);
289 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
290 public static MvcHtmlString TextBoxFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
, object htmlAttributes
) {
291 return htmlHelper
.TextBoxFor(expression
, HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes
));
294 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification
= "This is an appropriate nesting of generic types")]
295 public static MvcHtmlString TextBoxFor
<TModel
, TProperty
>(this HtmlHelper
<TModel
> htmlHelper
, Expression
<Func
<TModel
, TProperty
>> expression
, IDictionary
<string, object> htmlAttributes
) {
296 ModelMetadata metadata
= ModelMetadata
.FromLambdaExpression(expression
, htmlHelper
.ViewData
);
297 return TextBoxHelper(htmlHelper
,
300 ExpressionHelper
.GetExpressionText(expression
),
304 private static MvcHtmlString
TextBoxHelper(this HtmlHelper htmlHelper
, ModelMetadata metadata
, object model
, string expression
, IDictionary
<string, object> htmlAttributes
) {
305 return InputHelper(htmlHelper
, InputType
.Text
, metadata
, expression
, model
, false /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes
);
310 private static MvcHtmlString
InputHelper(HtmlHelper htmlHelper
, InputType inputType
, ModelMetadata metadata
, string name
, object value, bool useViewData
, bool isChecked
, bool setId
, bool isExplicitValue
, IDictionary
<string, object> htmlAttributes
) {
311 string fullName
= htmlHelper
.ViewContext
.ViewData
.TemplateInfo
.GetFullHtmlFieldName(name
);
312 if (String
.IsNullOrEmpty(fullName
)) {
313 throw new ArgumentException(MvcResources
.Common_NullOrEmpty
, "name");
316 TagBuilder tagBuilder
= new TagBuilder("input");
317 tagBuilder
.MergeAttributes(htmlAttributes
);
318 tagBuilder
.MergeAttribute("type", HtmlHelper
.GetInputTypeString(inputType
));
319 tagBuilder
.MergeAttribute("name", fullName
, true);
321 string valueParameter
= Convert
.ToString(value, CultureInfo
.CurrentCulture
);
322 bool usedModelState
= false;
325 case InputType
.CheckBox
:
326 bool? modelStateWasChecked
= htmlHelper
.GetModelStateValue(fullName
, typeof(bool)) as bool?;
327 if (modelStateWasChecked
.HasValue
) {
328 isChecked
= modelStateWasChecked
.Value
;
329 usedModelState
= true;
331 goto case InputType
.Radio
;
332 case InputType
.Radio
:
333 if (!usedModelState
) {
334 string modelStateValue
= htmlHelper
.GetModelStateValue(fullName
, typeof(string)) as string;
335 if (modelStateValue
!= null) {
336 isChecked
= String
.Equals(modelStateValue
, valueParameter
, StringComparison
.Ordinal
);
337 usedModelState
= true;
340 if (!usedModelState
&& useViewData
) {
341 isChecked
= htmlHelper
.EvalBoolean(fullName
);
344 tagBuilder
.MergeAttribute("checked", "checked");
346 tagBuilder
.MergeAttribute("value", valueParameter
, isExplicitValue
);
348 case InputType
.Password
:
350 tagBuilder
.MergeAttribute("value", valueParameter
, isExplicitValue
);
354 string attemptedValue
= (string)htmlHelper
.GetModelStateValue(fullName
, typeof(string));
355 tagBuilder
.MergeAttribute("value", attemptedValue
?? ((useViewData
) ? htmlHelper
.EvalString(fullName
) : valueParameter
), isExplicitValue
);
360 tagBuilder
.GenerateId(fullName
);
363 // If there are any errors for a named field, we add the css attribute.
364 ModelState modelState
;
365 if (htmlHelper
.ViewData
.ModelState
.TryGetValue(fullName
, out modelState
)) {
366 if (modelState
.Errors
.Count
> 0) {
367 tagBuilder
.AddCssClass(HtmlHelper
.ValidationInputCssClassName
);
371 tagBuilder
.MergeAttributes(htmlHelper
.GetUnobtrusiveValidationAttributes(name
, metadata
));
373 if (inputType
== InputType
.CheckBox
) {
374 // Render an additional <input type="hidden".../> for checkboxes. This
375 // addresses scenarios where unchecked checkboxes are not sent in the request.
376 // Sending a hidden input makes it possible to know that the checkbox was present
377 // on the page when the request was submitted.
378 StringBuilder inputItemBuilder
= new StringBuilder();
379 inputItemBuilder
.Append(tagBuilder
.ToString(TagRenderMode
.SelfClosing
));
381 TagBuilder hiddenInput
= new TagBuilder("input");
382 hiddenInput
.MergeAttribute("type", HtmlHelper
.GetInputTypeString(InputType
.Hidden
));
383 hiddenInput
.MergeAttribute("name", fullName
);
384 hiddenInput
.MergeAttribute("value", "false");
385 inputItemBuilder
.Append(hiddenInput
.ToString(TagRenderMode
.SelfClosing
));
386 return MvcHtmlString
.Create(inputItemBuilder
.ToString());
389 return tagBuilder
.ToMvcHtmlString(TagRenderMode
.SelfClosing
);
392 private static RouteValueDictionary
ToRouteValueDictionary(IDictionary
<string, object> dictionary
) {
393 return dictionary
== null ? new RouteValueDictionary() : new RouteValueDictionary(dictionary
);