Can I add a CSS class definition to the Html.LabelFor in MVC3? - asp.net-mvc

I hope someone out there has some ideas. I would like to tidy up my code and so I already used the Html.LabelFor. However now I want to assign a CSS class to the labels.
Html.LabelFor(model => model.Customer.Description ????)
Does anyone out there know if this is possible in MVC3. Note it's MVC3 I am using. Already I saw a post that talked about MVC2 and there being no simple solution.

Here you go buddy-o:
namespace System.Web.Mvc.Html
{
using System;
using Collections.Generic;
using Linq;
using Linq.Expressions;
using Mvc;
public static class LabelExtensions
{
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
return html.LabelFor(expression, null, htmlAttributes);
}
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, object htmlAttributes)
{
return html.LabelHelper(
ModelMetadata.FromLambdaExpression(expression, html.ViewData),
ExpressionHelper.GetExpressionText(expression),
HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes),
labelText);
}
private static MvcHtmlString LabelHelper(this HtmlHelper html, ModelMetadata metadata, string htmlFieldName, IDictionary<string, object> htmlAttributes, string labelText = null)
{
var str = labelText
?? (metadata.DisplayName
?? (metadata.PropertyName
?? htmlFieldName.Split(new[] { '.' }).Last()));
if (string.IsNullOrEmpty(str))
return MvcHtmlString.Empty;
var tagBuilder = new TagBuilder("label");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.Attributes.Add("for", TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName)));
tagBuilder.SetInnerText(str);
return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
}
private static MvcHtmlString ToMvcHtmlString(this TagBuilder tagBuilder, TagRenderMode renderMode)
{
return new MvcHtmlString(tagBuilder.ToString(renderMode));
}
}
}

There is no built in way to do this in MVC 3. You will have to write your helper that does this. Take a look at the LabelExtensions class to see how it is done.

Related

Rendering special characters in readonly input field

In ASP.NET MVC application i'm using TagBuilder to create readonly input field.
public class DisplayTextFieldBuilder<TModel, TProperty>
{
private HtmlHelper<TModel> _helper;
private Expression<Func<TModel, TProperty>> _expression;
public DisplayTextFieldBuilder(HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
_helper = helper;
_expression = expression;
}
public override string ToString()
{
var id = _helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(_expression));
var name = _helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(_expression));
var value = HttpUtility.HtmlEncode(_helper.DisplayFor(_expression).ToString());
TagBuilder builder = new TagBuilder("input");
builder.MergeAttribute("value", value);
builder.MergeAttribute("id", id);
builder.MergeAttribute("name", name);
builder.MergeAttribute("readonly", "readonly");
var html = builder.ToString(TagRenderMode.SelfClosing);
return html;
}
However certain string does not render properly. For example, lets say the model property value is Foe`s BBQ (note it has apostrophe)
then
HttpUtility.HtmlEncode(_helper.DisplayFor(_expression).ToString())
renders this value as Foe&#39;s, BBQ
if i remove html encoding then
_helper.DisplayFor(_expression).ToString()
renders this value as Foe's, BBQ
what i am missing?

Is there an existing MVC HtmlHelper like #Html.Label that generates a <span>?

I'm wondering if I need to create a custom HtmlHelper to simply display a value as a span.
Currently I'm using:
#model string
#{
var htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(ViewData["htmlAttributes"]);
}
<span class="#htmlAttributes["class"]">#Model</string>
I'm just wondering if there already exists something like #Html.Label so I could do:
#model string
#Html.SomethingLikeSpan("", Model, #ViewData["htmlAttributes"])
Not yet, but you can extend the Helpers object with any tag combination you wish.
Add a class file named HtmlHelperExtensions.cs with the following code:
using System;
using System.Web;
using System.Web.Mvc;
namespace WebApplication1
{
public static class HtmlHelperExtensions
{
public static IHtmlString Span(this HtmlHelper Helper, string Content, string Class = "")
{
string classstring = Class == "" ? "" : string.Format(" class=\"{0}\" ", Class);
string htmlString = String.Format("<span{1}>{0}</span>", Content, classstring);
return new HtmlString(htmlString);
}
}
}
Then in your view, use the following to format anything with the new helper extension:
#using WebApplication1
#Html.Span("Test Content")
#Html.Span("Test with class", "btn btn-primary")
I know this post is quite old but I'm going to post what I used to accomplish a very basic version of the #Html.Span and #Html.SpanFor helpers. I'll post for both .netcore and MVC5. I hope this helps people out!
MVC 5 implementation
using System;
using System.Linq.Expressions;
using System.Web.Mvc;
public static class HMTLHelperExtensions
{
public static MvcHtmlString Span(this HtmlHelper Helper, string Name, string Content, object HtmlAttributes)
{
var htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(HtmlAttributes);
TagBuilder tag = new TagBuilder("Span");
tag.MergeAttribute("name", TagBuilder.CreateSanitizedId(Name));
tag.GenerateId(Name);
tag.InnerHtml = Content;
foreach(var i in htmlAttributes )
{
tag.MergeAttribute(i.Key, i.Value.ToString());
}
return MvcHtmlString.Create(tag.ToString());
}
public static MvcHtmlString Span(this HtmlHelper Helper, string Name, string Content)
{
TagBuilder tag = new TagBuilder("Span");
tag.MergeAttribute("name", TagBuilder.CreateSanitizedId(Name));
tag.GenerateId(Name);
tag.InnerHtml = Content;
return MvcHtmlString.Create(tag.ToString());
}
public static MvcHtmlString SpanFor<TModel, TProperty>(this HtmlHelper<TModel> Helper, Expression<Func<TModel, TProperty>> expression, string Content, object HtmlAttributes)
{
var name = ExpressionHelper.GetExpressionText(expression);
var metaData = ModelMetadata.FromLambdaExpression(expression, Helper.ViewData);
return Span(Helper, name, metaData.Model as string, HtmlAttributes);
}
public static MvcHtmlString SpanFor<TModel, TProperty>(this HtmlHelper<TModel> Helper, Expression<Func<TModel, TProperty>> expression, string Content)
{
var name = ExpressionHelper.GetExpressionText(expression);
var metaData = ModelMetadata.FromLambdaExpression(expression, Helper.ViewData);
return Span(Helper, name, metaData.Model as string);
}
}
and here is the .netcore implementation
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using System;
using System.Linq.Expressions;
public static class HtmlHelperExtensions
{
public static IHtmlContent Span(this IHtmlHelper htmlHelper, string name, string Content, object htmlAttributes)
{
TagBuilder tag = new TagBuilder("Span");
tag.MergeAttribute("name", TagBuilder.CreateSanitizedId(name,""));
tag.GenerateId(name, "");
tag.InnerHtml.Append(Content);
var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
foreach (var i in attributes)
{
tag.MergeAttribute(i.Key, i.Value.ToString());
}
return tag;
}
public static IHtmlContent Span(this IHtmlHelper htmlHelper, string name, string Content)
{
TagBuilder tag = new TagBuilder("Span");
tag.MergeAttribute("name", TagBuilder.CreateSanitizedId(name, ""));
tag.GenerateId(name, "");
tag.InnerHtml.Append(Content);
return tag;
}
public static IHtmlContent SpanFor<TModel, TProperty>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel,TProperty>> expression, string content, object htmlAttributes)
{
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, htmlHelper.ViewData, htmlHelper.MetadataProvider);
var name = ExpressionHelper.GetExpressionText(expression);
var metaData = modelExplorer.Metadata;
return Span(htmlHelper, name, modelExplorer.Model as string, htmlAttributes);
}
public static IHtmlContent SpanFor<TModel, TProperty>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string content)
{
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, htmlHelper.ViewData, htmlHelper.MetadataProvider);
var name = ExpressionHelper.GetExpressionText(expression);
var metaData = modelExplorer.Metadata;
return Span(htmlHelper, name, modelExplorer.Model as string);
}
}
I want to point out, these will work with html attributes only if used in this format.
#Html.Span("Test", "Testing Content", new { #class = "test-class", style = "text-align:right" }
#Html.SpanFor(x=>x.ModelProperty, Model.ModelProperty, new { #class = "test-class", style = "text-align:right" }

Access ModelState from within my custom helper?

I just want to make a such Helper:
public static MvcHtmlString HasError<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
StringBuilder sb = new StringBuilder();
if (!ModelState.IsValidField(ExpressionHelper.GetExpressionText(expression)))
sb.Append("has-error");
return MvcHtmlString.Create(sb.ToString());
}
So the question - how to access to the actual ModelState here?
You can use the following code:
foreach (var state in htmlHelper.ViewData.ModelState)
{
// Do what you what with the ModelState here
foreach (var error in state.Value.Errors)
{
// Display error here
}
}

MVC3 Extension for ValidatorMessage

i have wrote a extension method for customize my validation messages, like this:
namespace Helpers
{
public static class HtmlHelpers
{
public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
var sb = new StringBuilder();
string modelName = ExpressionHelper.GetExpressionText(expression);
ModelState state = htmlHelper.ViewData.ModelState[modelName];
if (state != null)
if ((state.Errors != null) && (state.Errors.Count > 0))
{
sb.Append("<div class='error-left'></div>");
sb.Append("<div class='error-inner'>");
sb.Append(htmlHelper.ValidationMessageFor(expression).ToString());
sb.Append("</div>");
}
return MvcHtmlString.Create(sb.ToString());
}
}
}
So in my view, i put
#using HtmlHelpers
and also
#Html.ValidationMessageFor(model => model.Name)
But i'm getting this exception:
The call is ambiguous between the following methods or properties: 'ContinentalWeb.Helpers.HtmlHelpers.ValidationMessageFor<ContinentalWeb.Models.Maker,string>(System.Web.Mvc.HtmlHelper<Type>, System.Linq.Expressions.Expression<System.Func<Type,string>>)' and 'System.Web.Mvc.Html.ValidationExtensions.ValidationMessageFor<Type,string>(System.Web.Mvc.HtmlHelper<Type>, System.Linq.Expressions.Expression<System.Func<Type,string>>)'
I'm new in MVC... any help?
Thank you!
Your extension method has the same name and signature as the default one. This is not possible because you cannot have the 2 at the same time in scope. You will have to give a different name to your method or change the number and/or type of arguments to avoid the conflict.
For example:
public static MvcHtmlString CustomValidationMessageFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression
)
{
...
}
and then use it like this:
#Html.CustomValidationMessageFor(model => model.Name)

How do I prevent MVC3 html escaping my validation messages?

I'm trying to output a validation message in MVC3 which contains a link.
I'm outputting the error message place holder like this
#Html.ValidationMessageFor(model => model.Email)
The problem is, the error message is html escaped which is fine most times, but I'd like a link to be in the middle.
<span class="field-validation-error" data-valmsg-for="Email" data-valmsg-replace="true">This e-mail address is already registed. <a href="%url_token%">Click here to reset.</a></span>
How do I prevent this from happening?
This works but is not a solution, rather, a temporary work around.
#{
string s = Html.ValidationMessageFor(model => model.Email).ToString();
}
#Html.Raw(HttpUtility.HtmlDecode(s))
Looking at the code with reflector it doesn't seem to have a method for that, the more complete overload is:
public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
string validationMessage,
object htmlAttributes)
{
return htmlHelper.ValidationMessageFor<TModel, TProperty>(expression, validationMessage, ((IDictionary<string, object>) HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)));
}
What you can do however is to create an extension method that returns the string as you want, like this:
public static class ValidationExtender
{
public static IHtmlString HtmlValidationMessageFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
return MvcHtmlString.Create(htmlHelper.ValidationMessageFor(expression).ToHtmlString());
}
}
You can use a MvcHtmlString. See MvcHtmlString.Create(). It will output the html without escaping.
Taking #BrunoLM's lead, The following extension methods should give you what you need. I've only done basic testing on this, but it does work.
public static class HtmlHelperExtensions
{
private static readonly string htmlErrorPlaceholder = "##__html#Error#Placeholder__##";
public static IHtmlString HtmlValidationMessageFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
object htmlAttributes)
{
var name = expression.GetMemberName();
var isInError = htmlHelper.ViewContext.ViewData.ModelState.ContainsKey(name);
var message = htmlHelper.ValidationMessageFor(expression, htmlErrorPlaceholder, htmlAttributes);
if (isInError && !MvcHtmlString.IsNullOrEmpty(message))
{
var realError = htmlHelper.ViewContext.ViewData.ModelState[name].Errors.First().ErrorMessage;
return htmlHelper.Raw(message.ToString().Replace(htmlErrorPlaceholder, realError));
}
return MvcHtmlString.Empty;
}
}
public static class Expression_1Extensions
{
public static string GetMemberName<TModel, TProperty>(this Expression<Func<TModel, TProperty>> expression)
{
switch (expression.Body.NodeType)
{
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression)expression.Body;
return memberExpression.Member is PropertyInfo ? memberExpression.Member.Name : null;
}
throw new NotSupportedException();
}
}

Resources