1 namespace System.Web.Mvc.Html { 2 using System; 3 using System.Collections.Generic; 4 using System.Diagnostics.CodeAnalysis; 5 using System.Globalization; 6 using System.Linq.Expressions; 7 using System.Web.Mvc.Resources; 8 9 public static class TextAreaExtensions { 10 // These values are similar to the defaults used by WebForms 11 // when using <asp:TextBox TextMode="MultiLine"> without specifying 12 // the Rows and Columns attributes. 13 private const int TextAreaRows = 2; 14 private const int TextAreaColumns = 20; 15 private static Dictionary<string, object> implicitRowsAndColumns = new Dictionary<string, object> { 16 { "rows", TextAreaRows.ToString(CultureInfo.InvariantCulture) }, 17 { "cols", TextAreaColumns.ToString(CultureInfo.InvariantCulture) }, 18 }; 19 GetRowsAndColumnsDictionary(int rows, int columns)20 private static Dictionary<string, object> GetRowsAndColumnsDictionary(int rows, int columns) { 21 if (rows < 0) { 22 throw new ArgumentOutOfRangeException("rows", MvcResources.HtmlHelper_TextAreaParameterOutOfRange); 23 } 24 if (columns < 0) { 25 throw new ArgumentOutOfRangeException("columns", MvcResources.HtmlHelper_TextAreaParameterOutOfRange); 26 } 27 28 Dictionary<string, object> result = new Dictionary<string, object>(); 29 if (rows > 0) { 30 result.Add("rows", rows.ToString(CultureInfo.InvariantCulture)); 31 } 32 if (columns > 0) { 33 result.Add("cols", columns.ToString(CultureInfo.InvariantCulture)); 34 } 35 36 return result; 37 } 38 TextArea(this HtmlHelper htmlHelper, string name)39 public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name) { 40 return TextArea(htmlHelper, name, null /* value */, null /* htmlAttributes */); 41 } 42 TextArea(this HtmlHelper htmlHelper, string name, object htmlAttributes)43 public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, object htmlAttributes) { 44 return TextArea(htmlHelper, name, null /* value */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); 45 } 46 TextArea(this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes)47 public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes) { 48 return TextArea(htmlHelper, name, null /* value */, htmlAttributes); 49 } 50 TextArea(this HtmlHelper htmlHelper, string name, string value)51 public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value) { 52 return TextArea(htmlHelper, name, value, null /* htmlAttributes */); 53 } 54 TextArea(this HtmlHelper htmlHelper, string name, string value, object htmlAttributes)55 public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value, object htmlAttributes) { 56 return TextArea(htmlHelper, name, value, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); 57 } 58 TextArea(this HtmlHelper htmlHelper, string name, string value, IDictionary<string, object> htmlAttributes)59 public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value, IDictionary<string, object> htmlAttributes) { 60 ModelMetadata metadata = ModelMetadata.FromStringExpression(name, htmlHelper.ViewContext.ViewData); 61 if (value != null) { 62 metadata.Model = value; 63 } 64 65 return TextAreaHelper(htmlHelper, metadata, name, implicitRowsAndColumns, htmlAttributes); 66 } 67 TextArea(this HtmlHelper htmlHelper, string name, string value, int rows, int columns, object htmlAttributes)68 public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value, int rows, int columns, object htmlAttributes) { 69 return TextArea(htmlHelper, name, value, rows, columns, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); 70 } 71 TextArea(this HtmlHelper htmlHelper, string name, string value, int rows, int columns, IDictionary<string, object> htmlAttributes)72 public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value, int rows, int columns, IDictionary<string, object> htmlAttributes) { 73 ModelMetadata metadata = ModelMetadata.FromStringExpression(name, htmlHelper.ViewContext.ViewData); 74 if (value != null) { 75 metadata.Model = value; 76 } 77 78 return TextAreaHelper(htmlHelper, metadata, name, GetRowsAndColumnsDictionary(rows, columns), htmlAttributes); 79 } 80 81 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] TextAreaFor(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)82 public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) { 83 return TextAreaFor(htmlHelper, expression, (IDictionary<string, object>)null); 84 } 85 86 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] TextAreaFor(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)87 public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) { 88 return TextAreaFor(htmlHelper, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); 89 } 90 91 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] TextAreaFor(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)92 public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) { 93 if (expression == null) { 94 throw new ArgumentNullException("expression"); 95 } 96 97 return TextAreaHelper(htmlHelper, 98 ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData), 99 ExpressionHelper.GetExpressionText(expression), 100 implicitRowsAndColumns, 101 htmlAttributes); 102 } 103 104 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] TextAreaFor(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, int rows, int columns, object htmlAttributes)105 public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, int rows, int columns, object htmlAttributes) { 106 return TextAreaFor(htmlHelper, expression, rows, columns, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); 107 } 108 109 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] TextAreaFor(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, int rows, int columns, IDictionary<string, object> htmlAttributes)110 public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, int rows, int columns, IDictionary<string, object> htmlAttributes) { 111 if (expression == null) { 112 throw new ArgumentNullException("expression"); 113 } 114 115 return TextAreaHelper(htmlHelper, 116 ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData), 117 ExpressionHelper.GetExpressionText(expression), 118 GetRowsAndColumnsDictionary(rows, columns), 119 htmlAttributes); 120 } 121 122 [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "If this fails, it is because the string-based version had an empty 'name' parameter")] TextAreaHelper(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string name, IDictionary<string, object> rowsAndColumns, IDictionary<string, object> htmlAttributes)123 private static MvcHtmlString TextAreaHelper(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string name, IDictionary<string, object> rowsAndColumns, IDictionary<string, object> htmlAttributes) { 124 string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); 125 if (String.IsNullOrEmpty(fullName)) { 126 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name"); 127 } 128 129 TagBuilder tagBuilder = new TagBuilder("textarea"); 130 tagBuilder.GenerateId(fullName); 131 tagBuilder.MergeAttributes(htmlAttributes, true); 132 tagBuilder.MergeAttributes(rowsAndColumns, rowsAndColumns != implicitRowsAndColumns); // Only force explicit rows/cols 133 tagBuilder.MergeAttribute("name", fullName, true); 134 135 // If there are any errors for a named field, we add the CSS attribute. 136 ModelState modelState; 137 if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState) && modelState.Errors.Count > 0) { 138 tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName); 139 } 140 141 tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name)); 142 143 string value; 144 if (modelState != null && modelState.Value != null) { 145 value = modelState.Value.AttemptedValue; 146 } 147 else if (modelMetadata.Model != null) { 148 value = modelMetadata.Model.ToString(); 149 } 150 else { 151 value = String.Empty; 152 } 153 154 // The first newline is always trimmed when a TextArea is rendered, so we add an extra one 155 // in case the value being rendered is something like "\r\nHello". 156 tagBuilder.SetInnerText(Environment.NewLine + value); 157 158 return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal); 159 } 160 } 161 } 162