1 namespace System.Web.Mvc {
3 using System.Collections.Generic;
4 using System.ComponentModel;
5 using System.Diagnostics.CodeAnalysis;
6 using System.Globalization;
11 using System.Web.Helpers;
12 using System.Web.Mvc.Resources;
13 using System.Web.Routing;
15 public class HtmlHelper {
16 public static readonly string ValidationInputCssClassName = "input-validation-error";
17 public static readonly string ValidationInputValidCssClassName = "input-validation-valid";
18 public static readonly string ValidationMessageCssClassName = "field-validation-error";
19 public static readonly string ValidationMessageValidCssClassName = "field-validation-valid";
20 public static readonly string ValidationSummaryCssClassName = "validation-summary-errors";
21 public static readonly string ValidationSummaryValidCssClassName = "validation-summary-valid";
23 public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer)
24 : this(viewContext, viewDataContainer, RouteTable.Routes) {
27 public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection) {
28 if (viewContext == null) {
29 throw new ArgumentNullException("viewContext");
31 if (viewDataContainer == null) {
32 throw new ArgumentNullException("viewDataContainer");
34 if (routeCollection == null) {
35 throw new ArgumentNullException("routeCollection");
38 ViewContext = viewContext;
39 ViewDataContainer = viewDataContainer;
40 RouteCollection = routeCollection;
41 ClientValidationRuleFactory = (name, metadata) => ModelValidatorProviders.Providers.GetValidators(metadata ?? ModelMetadata.FromStringExpression(name, ViewData), ViewContext).SelectMany(v => v.GetClientValidationRules());
44 public static bool ClientValidationEnabled {
46 return ViewContext.GetClientValidationEnabled();
49 ViewContext.SetClientValidationEnabled(value);
53 public static string IdAttributeDotReplacement {
55 return System.Web.WebPages.Html.HtmlHelper.IdAttributeDotReplacement;
58 System.Web.WebPages.Html.HtmlHelper.IdAttributeDotReplacement = value;
62 internal Func<string, ModelMetadata, IEnumerable<ModelClientValidationRule>> ClientValidationRuleFactory {
67 public RouteCollection RouteCollection {
72 public static bool UnobtrusiveJavaScriptEnabled {
74 return ViewContext.GetUnobtrusiveJavaScriptEnabled();
77 ViewContext.SetUnobtrusiveJavaScriptEnabled(value);
81 public ViewContext ViewContext {
86 public ViewDataDictionary ViewData {
88 return ViewDataContainer.ViewData;
92 public IViewDataContainer ViewDataContainer {
97 public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes) {
98 RouteValueDictionary result = new RouteValueDictionary();
100 if (htmlAttributes != null) {
101 foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(htmlAttributes)) {
102 result.Add(property.Name.Replace('_', '-'), property.GetValue(htmlAttributes));
109 public MvcHtmlString AntiForgeryToken() {
110 return AntiForgeryToken(salt: null);
113 public MvcHtmlString AntiForgeryToken(string salt) {
114 return AntiForgeryToken(salt, domain: null, path: null);
117 public MvcHtmlString AntiForgeryToken(string salt, string domain, string path) {
118 //Disabled to compile MVC3 with the newer System.Web.WebPages helpers
119 //return new MvcHtmlString(AntiForgery.GetHtml(ViewContext.HttpContext, salt, domain, path).ToString());
120 return new MvcHtmlString(AntiForgery.GetHtml().ToString());
123 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")]
124 public string AttributeEncode(string value) {
125 return (!String.IsNullOrEmpty(value)) ? HttpUtility.HtmlAttributeEncode(value) : String.Empty;
128 public string AttributeEncode(object value) {
129 return AttributeEncode(Convert.ToString(value, CultureInfo.InvariantCulture));
132 public void EnableClientValidation() {
133 EnableClientValidation(enabled: true);
136 public void EnableClientValidation(bool enabled) {
137 ViewContext.ClientValidationEnabled = enabled;
140 public void EnableUnobtrusiveJavaScript() {
141 EnableUnobtrusiveJavaScript(enabled: true);
144 public void EnableUnobtrusiveJavaScript(bool enabled) {
145 ViewContext.UnobtrusiveJavaScriptEnabled = enabled;
148 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")]
149 public string Encode(string value) {
150 return (!String.IsNullOrEmpty(value)) ? HttpUtility.HtmlEncode(value) : String.Empty;
153 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")]
154 public string Encode(object value) {
155 return value != null ? HttpUtility.HtmlEncode(value) : String.Empty;
158 internal string EvalString(string key) {
159 return Convert.ToString(ViewData.Eval(key), CultureInfo.CurrentCulture);
162 internal bool EvalBoolean(string key) {
163 return Convert.ToBoolean(ViewData.Eval(key), CultureInfo.InvariantCulture);
166 internal static IView FindPartialView(ViewContext viewContext, string partialViewName, ViewEngineCollection viewEngineCollection) {
167 ViewEngineResult result = viewEngineCollection.FindPartialView(viewContext, partialViewName);
168 if (result.View != null) {
172 StringBuilder locationsText = new StringBuilder();
173 foreach (string location in result.SearchedLocations) {
174 locationsText.AppendLine();
175 locationsText.Append(location);
178 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
179 MvcResources.Common_PartialViewNotFound, partialViewName, locationsText));
182 public static string GenerateIdFromName(string name) {
183 return GenerateIdFromName(name, IdAttributeDotReplacement);
186 public static string GenerateIdFromName(string name, string idAttributeDotReplacement) {
188 throw new ArgumentNullException("name");
191 if (idAttributeDotReplacement == null) {
192 throw new ArgumentNullException("idAttributeDotReplacement");
195 // TagBuilder.CreateSanitizedId returns null for empty strings, return String.Empty instead to avoid breaking change
196 if (name.Length == 0) {
200 return TagBuilder.CreateSanitizedId(name, idAttributeDotReplacement);
203 public static string GenerateLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
204 return GenerateLink(requestContext, routeCollection, linkText, routeName, actionName, controllerName, null/* protocol */, null/* hostName */, null/* fragment */, routeValues, htmlAttributes);
207 public static string GenerateLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
208 return GenerateLinkInternal(requestContext, routeCollection, linkText, routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, htmlAttributes, true /* includeImplicitMvcValues */);
211 private static string GenerateLinkInternal(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool includeImplicitMvcValues) {
212 string url = UrlHelper.GenerateUrl(routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, routeCollection, requestContext, includeImplicitMvcValues);
213 TagBuilder tagBuilder = new TagBuilder("a") {
214 InnerHtml = (!String.IsNullOrEmpty(linkText)) ? HttpUtility.HtmlEncode(linkText) : String.Empty
216 tagBuilder.MergeAttributes(htmlAttributes);
217 tagBuilder.MergeAttribute("href", url);
218 return tagBuilder.ToString(TagRenderMode.Normal);
221 public static string GenerateRouteLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
222 return GenerateRouteLink(requestContext, routeCollection, linkText, routeName, null/* protocol */, null/* hostName */, null/* fragment */, routeValues, htmlAttributes);
225 public static string GenerateRouteLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
226 return GenerateLinkInternal(requestContext, routeCollection, linkText, routeName, null /* actionName */, null /* controllerName */, protocol, hostName, fragment, routeValues, htmlAttributes, false /* includeImplicitMvcValues */);
229 public static string GetFormMethodString(FormMethod method) {
233 case FormMethod.Post:
240 public static string GetInputTypeString(InputType inputType) {
242 case InputType.CheckBox:
244 case InputType.Hidden:
246 case InputType.Password:
248 case InputType.Radio:
257 internal object GetModelStateValue(string key, Type destinationType) {
258 ModelState modelState;
259 if (ViewData.ModelState.TryGetValue(key, out modelState)) {
260 if (modelState.Value != null) {
261 return modelState.Value.ConvertTo(destinationType, null /* culture */);
267 public IDictionary<string, object> GetUnobtrusiveValidationAttributes(string name) {
268 return GetUnobtrusiveValidationAttributes(name, metadata: null);
271 // Only render attributes if unobtrusive client-side validation is enabled, and then only if we've
272 // never rendered validation for a field with this name in this form. Also, if there's no form context,
273 // then we can't render the attributes (we'd have no <form> to attach them to).
274 public IDictionary<string, object> GetUnobtrusiveValidationAttributes(string name, ModelMetadata metadata) {
275 Dictionary<string, object> results = new Dictionary<string, object>();
277 // The ordering of these 3 checks (and the early exits) is for performance reasons.
278 if (!ViewContext.UnobtrusiveJavaScriptEnabled) {
282 FormContext formContext = ViewContext.GetFormContextForClientValidation();
283 if (formContext == null) {
287 string fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
288 if (formContext.RenderedField(fullName)) {
292 formContext.RenderedField(fullName, true);
294 IEnumerable<ModelClientValidationRule> clientRules = ClientValidationRuleFactory(name, metadata);
295 bool renderedRules = false;
297 foreach (ModelClientValidationRule rule in clientRules) {
298 renderedRules = true;
299 string ruleName = "data-val-" + rule.ValidationType;
301 ValidateUnobtrusiveValidationRule(rule, results, ruleName);
303 results.Add(ruleName, HttpUtility.HtmlEncode(rule.ErrorMessage ?? String.Empty));
306 foreach (var kvp in rule.ValidationParameters) {
307 results.Add(ruleName + kvp.Key, kvp.Value ?? String.Empty);
312 results.Add("data-val", "true");
318 public MvcHtmlString HttpMethodOverride(HttpVerbs httpVerb) {
321 case HttpVerbs.Delete:
322 httpMethod = "DELETE";
331 throw new ArgumentException(MvcResources.HtmlHelper_InvalidHttpVerb, "httpVerb");
334 return HttpMethodOverride(httpMethod);
337 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")]
338 public MvcHtmlString HttpMethodOverride(string httpMethod) {
339 if (String.IsNullOrEmpty(httpMethod)) {
340 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "httpMethod");
342 if (String.Equals(httpMethod, "GET", StringComparison.OrdinalIgnoreCase) ||
343 String.Equals(httpMethod, "POST", StringComparison.OrdinalIgnoreCase)) {
344 throw new ArgumentException(MvcResources.HtmlHelper_InvalidHttpMethod, "httpMethod");
347 TagBuilder tagBuilder = new TagBuilder("input");
348 tagBuilder.Attributes["type"] = "hidden";
349 tagBuilder.Attributes["name"] = HttpRequestExtensions.XHttpMethodOverrideKey;
350 tagBuilder.Attributes["value"] = httpMethod;
352 return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
356 /// Wraps HTML markup in an IHtmlString, which will enable HTML markup to be
357 /// rendered to the output without getting HTML encoded.
359 /// <param name="value">HTML markup string.</param>
360 /// <returns>An IHtmlString that represents HTML markup.</returns>
361 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "For consistency, all helpers are instance methods.")]
362 public IHtmlString Raw(string value) {
363 return new HtmlString(value);
366 internal virtual void RenderPartialInternal(string partialViewName, ViewDataDictionary viewData, object model, TextWriter writer, ViewEngineCollection viewEngineCollection) {
367 if (String.IsNullOrEmpty(partialViewName)) {
368 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName");
371 ViewDataDictionary newViewData = null;
374 if (viewData == null) {
375 newViewData = new ViewDataDictionary(ViewData);
378 newViewData = new ViewDataDictionary(viewData);
382 if (viewData == null) {
383 newViewData = new ViewDataDictionary(model);
386 newViewData = new ViewDataDictionary(viewData) { Model = model };
390 ViewContext newViewContext = new ViewContext(ViewContext, ViewContext.View, newViewData, ViewContext.TempData, writer);
391 IView view = FindPartialView(newViewContext, partialViewName, viewEngineCollection);
392 view.Render(newViewContext, writer);
395 private static void ValidateUnobtrusiveValidationRule(ModelClientValidationRule rule, Dictionary<string, object> resultsDictionary, string dictionaryKey) {
396 if (String.IsNullOrWhiteSpace(rule.ValidationType)) {
397 throw new InvalidOperationException(
399 CultureInfo.CurrentCulture,
400 MvcResources.HtmlHelper_ValidationTypeCannotBeEmpty,
401 rule.GetType().FullName
406 if (resultsDictionary.ContainsKey(dictionaryKey)) {
407 throw new InvalidOperationException(
409 CultureInfo.CurrentCulture,
410 MvcResources.HtmlHelper_ValidationTypeMustBeUnique,
416 if (rule.ValidationType.Any(c => !Char.IsLower(c))) {
417 throw new InvalidOperationException(
419 CultureInfo.CurrentCulture,
420 MvcResources.HtmlHelper_ValidationTypeMustBeLegal,
422 rule.GetType().FullName
427 foreach (var key in rule.ValidationParameters.Keys) {
428 if (String.IsNullOrWhiteSpace(key)) {
429 throw new InvalidOperationException(
431 CultureInfo.CurrentCulture,
432 MvcResources.HtmlHelper_ValidationParameterCannotBeEmpty,
433 rule.GetType().FullName
438 if (!Char.IsLower(key.First()) || key.Any(c => !Char.IsLower(c) && !Char.IsDigit(c))) {
439 throw new InvalidOperationException(
441 CultureInfo.CurrentCulture,
442 MvcResources.HtmlHelper_ValidationParameterMustBeLegal,
444 rule.GetType().FullName