MVC3 integrated, with some changes to make it compile on Mono and with Razor2
[mono-project.git] / mcs / class / System.Web.Mvc3 / Mvc / Html / InputExtensions.cs
blob7c7aa6b0558ffca335b95c01e9f83bbd08b631d8
1 namespace System.Web.Mvc.Html {
2 using System;
3 using System.Collections.Generic;
4 using System.Data.Linq;
5 using System.Diagnostics.CodeAnalysis;
6 using System.Globalization;
7 using System.Linq.Expressions;
8 using System.Text;
9 using System.Web.Mvc.Resources;
10 using System.Web.Routing;
12 public static class InputExtensions {
13 // CheckBox
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) {
58 bool modelChecked;
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;
71 if (explicitValue) {
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);
78 // Hidden
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,
94 null,
95 value,
96 value == null /* useViewData */,
97 name,
98 htmlAttributes);
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,
115 metadata,
116 metadata.Model,
117 false,
118 ExpressionHelper.GetExpressionText(expression),
119 htmlAttributes);
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);
136 // Password
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),
174 null /* value */,
175 htmlAttributes);
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);
182 // RadioButton
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) {
214 if (value == null) {
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,
237 metadata,
238 metadata.Model,
239 ExpressionHelper.GetExpressionText(expression),
240 value,
241 null /* isChecked */,
242 htmlAttributes);
245 private static MvcHtmlString RadioButtonHelper(HtmlHelper htmlHelper, ModelMetadata metadata, object model, string name, object value, bool? isChecked, IDictionary<string, object> htmlAttributes) {
246 if (value == null) {
247 throw new ArgumentNullException("value");
250 RouteValueDictionary attributes = ToRouteValueDictionary(htmlAttributes);
252 bool explicitValue = isChecked.HasValue;
253 if (explicitValue) {
254 attributes.Remove("checked"); // Explicit value must override dictionary
256 else {
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);
266 // TextBox
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,
298 metadata,
299 metadata.Model,
300 ExpressionHelper.GetExpressionText(expression),
301 htmlAttributes);
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);
308 // Helper methods
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;
324 switch (inputType) {
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);
343 if (isChecked) {
344 tagBuilder.MergeAttribute("checked", "checked");
346 tagBuilder.MergeAttribute("value", valueParameter, isExplicitValue);
347 break;
348 case InputType.Password:
349 if (value != null) {
350 tagBuilder.MergeAttribute("value", valueParameter, isExplicitValue);
352 break;
353 default:
354 string attemptedValue = (string)htmlHelper.GetModelStateValue(fullName, typeof(string));
355 tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(fullName) : valueParameter), isExplicitValue);
356 break;
359 if (setId) {
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);