Using MVC 4 extensions in MVC 3 - asp.net-mvc-3

I want to use Html.LabelFor() extension of Asp. Net MVC 4 to add html attributes to a Label. I'm using MVC 3. My question is: Can I simple upgrade my system.web.mvc dll?(any problem with this?...) OR it will be a problem in deploy?

this is mvc3 source code
internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string labelText = null) {
string resolvedLabelText = labelText ?? metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(resolvedLabelText)) {
return MvcHtmlString.Empty;
}
TagBuilder tag = new TagBuilder("label");
tag.Attributes.Add("for", TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName)));
tag.SetInnerText(resolvedLabelText);
return tag.ToMvcHtmlString(TagRenderMode.Normal);}
and this is in mvc4
internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string labelText = null, IDictionary<string, object> htmlAttributes = null){
string resolvedLabelText = labelText ?? metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(resolvedLabelText))
{
return MvcHtmlString.Empty;
}
TagBuilder tag = new TagBuilder("label");
tag.Attributes.Add("for", TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName)));
tag.SetInnerText(resolvedLabelText);
tag.MergeAttributes(htmlAttributes, replaceExisting: true);
return tag.ToMvcHtmlString(TagRenderMode.Normal);}
there is only one line diffrenct tag.MergeAttributes(htmlAttributes, replaceExisting: true);
in mvc3 InputExtensions.cs you can find the TagBuilder.MergeAttributes function has used.
so you can write a extension method by youself,not need to upgrad you project.
below is the all code in mvc4,maybe it will help you
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
namespace System.Web.Mvc.Html
{
public static class LabelExtensions
{
public static MvcHtmlString Label(this HtmlHelper html, string expression)
{
return Label(html,
expression,
labelText: null);
}
public static MvcHtmlString Label(this HtmlHelper html, string expression, string labelText)
{
return Label(html, expression, labelText, htmlAttributes: null, metadataProvider: null);
}
public static MvcHtmlString Label(this HtmlHelper html, string expression, object htmlAttributes)
{
return Label(html, expression, labelText: null, htmlAttributes: htmlAttributes, metadataProvider: null);
}
public static MvcHtmlString Label(this HtmlHelper html, string expression, IDictionary<string, object> htmlAttributes)
{
return Label(html, expression, labelText: null, htmlAttributes: htmlAttributes, metadataProvider: null);
}
public static MvcHtmlString Label(this HtmlHelper html, string expression, string labelText, object htmlAttributes)
{
return Label(html, expression, labelText, htmlAttributes, metadataProvider: null);
}
public static MvcHtmlString Label(this HtmlHelper html, string expression, string labelText, IDictionary<string, object> htmlAttributes)
{
return Label(html, expression, labelText, htmlAttributes, metadataProvider: null);
}
internal static MvcHtmlString Label(this HtmlHelper html, string expression, string labelText, object htmlAttributes, ModelMetadataProvider metadataProvider)
{
return Label(html,
expression,
labelText,
HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes),
metadataProvider);
}
internal static MvcHtmlString Label(this HtmlHelper html, string expression, string labelText, IDictionary<string, object> htmlAttributes, ModelMetadataProvider metadataProvider)
{
return LabelHelper(html,
ModelMetadata.FromStringExpression(expression, html.ViewData, metadataProvider),
expression,
labelText,
htmlAttributes);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
return LabelFor<TModel, TValue>(html, expression, labelText: null);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText)
{
return LabelFor(html, expression, labelText, htmlAttributes: null, metadataProvider: null);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
return LabelFor(html, expression, labelText: null, htmlAttributes: htmlAttributes, metadataProvider: null);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
{
return LabelFor(html, expression, labelText: null, htmlAttributes: htmlAttributes, metadataProvider: null);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, object htmlAttributes)
{
return LabelFor(html, expression, labelText, htmlAttributes, metadataProvider: null);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, IDictionary<string, object> htmlAttributes)
{
return LabelFor(html, expression, labelText, htmlAttributes, metadataProvider: null);
}
internal static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, object htmlAttributes, ModelMetadataProvider metadataProvider)
{
return LabelFor(html,
expression,
labelText,
HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes),
metadataProvider);
}
internal static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, IDictionary<string, object> htmlAttributes, ModelMetadataProvider metadataProvider)
{
return LabelHelper(html,
ModelMetadata.FromLambdaExpression(expression, html.ViewData, metadataProvider),
ExpressionHelper.GetExpressionText(expression),
labelText,
htmlAttributes);
}
public static MvcHtmlString LabelForModel(this HtmlHelper html)
{
return LabelForModel(html, labelText: null);
}
public static MvcHtmlString LabelForModel(this HtmlHelper html, string labelText)
{
return LabelHelper(html, html.ViewData.ModelMetadata, String.Empty, labelText);
}
public static MvcHtmlString LabelForModel(this HtmlHelper html, object htmlAttributes)
{
return LabelHelper(html, html.ViewData.ModelMetadata, String.Empty, labelText: null, htmlAttributes: HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString LabelForModel(this HtmlHelper html, IDictionary<string, object> htmlAttributes)
{
return LabelHelper(html, html.ViewData.ModelMetadata, String.Empty, labelText: null, htmlAttributes: htmlAttributes);
}
public static MvcHtmlString LabelForModel(this HtmlHelper html, string labelText, object htmlAttributes)
{
return LabelHelper(html, html.ViewData.ModelMetadata, String.Empty, labelText, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
public static MvcHtmlString LabelForModel(this HtmlHelper html, string labelText, IDictionary<string, object> htmlAttributes)
{
return LabelHelper(html, html.ViewData.ModelMetadata, String.Empty, labelText, htmlAttributes);
}
internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string labelText = null, IDictionary<string, object> htmlAttributes = null)
{
string resolvedLabelText = labelText ?? metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(resolvedLabelText))
{
return MvcHtmlString.Empty;
}
TagBuilder tag = new TagBuilder("label");
tag.Attributes.Add("for", TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName)));
tag.SetInnerText(resolvedLabelText);
tag.MergeAttributes(htmlAttributes, replaceExisting: true);
return tag.ToMvcHtmlString(TagRenderMode.Normal);
}
}
}

Related

Custom Timeline items in Glimpse

I've only just started looking a profiling for an MVC3 application.
I found MiniProfiler first, and then found recommendations for using Glimpse. Both seem great, and I'd rather use Glimpse, but I'd like to add entries to the timeline for specific actions.
MiniProfiler has a nice feature where you can grab the current MiniProfiler context and add a step with the using() command. Is there a similar thing with Glimpse?
I did find an entry with someone explaining how to do it the long way, but wondered if there might have been a shorter way to do this, since then.
Thanks to this stackoverflow question, I found a gist that implements an equivalent of Glimpse v2's GlimpseTimeline for Glimpse v1.
From your code call:
using (Timeline.Capture("FormulaEvaluator.Evalauate"))
{
// Code to time
}
And here's the implementation of Timeline.Capture:
public static class Timeline
{
public static IDisposable Capture(string eventName)
{
#pragma warning disable 618
var timer = GlimpseConfiguration.GetConfiguredTimerStrategy()();
if (timer == null)
return null;
var broker = GlimpseConfiguration.GetConfiguredMessageBroker();
if (broker == null)
return null;
#pragma warning restore 618
return new TimelineCapture(timer, broker, eventName);
}
}
public class TimelineCapture : IDisposable
{
private readonly string _eventName;
private readonly IExecutionTimer _timer;
private readonly IMessageBroker _broker;
private readonly TimeSpan _startOffset;
public TimelineCapture(IExecutionTimer timer, IMessageBroker broker, string eventName)
{
_timer = timer;
_broker = broker;
_eventName = eventName;
_startOffset = _timer.Start();
}
public void Dispose()
{
_broker.Publish(new TimelineMessage(_eventName, _timer.Stop(_startOffset)));
}
}
public class TimelineMessage : ITimelineMessage
{
private static readonly TimelineCategoryItem DefaultCategory = new TimelineCategoryItem("MyApp", "green", "blue");
public TimelineMessage(string eventName, TimerResult result)
{
Id = Guid.NewGuid();
EventName = eventName;
EventCategory = DefaultCategory;
Offset = result.Offset;
StartTime = result.StartTime;
Duration = result.Duration;
}
public Guid Id { get; private set; }
public TimeSpan Offset { get; set; }
public TimeSpan Duration { get; set; }
public DateTime StartTime { get; set; }
public string EventName { get; set; }
public TimelineCategoryItem EventCategory { get; set; }
public string EventSubText { get; set; }
}
The Google group thread you mentioned is currently the approach that is being developed in version-2 of Glimpse. There you can find the GlimpseTimeline class that does what you want.
You could take this class and copy it in your own project while waiting for v2 to be released. Just keep in mind that you need to alter it a little bit, as some things like the MessageBroker are to be retrieved from the GlimpseConfiguration.GetConfiguredMessageBroker() etc...
But it might suit your needs for now...
I made a mashup of droyads gist and GlimpseTimeline from v2. GlimpseTimeline v1 gist
public class TimelineMessage : ITimelineMessage
{
public TimelineMessage()
{
Id = Guid.NewGuid();
}
public Guid Id { get; private set; }
public TimeSpan Offset { get; set; }
public TimeSpan Duration { get; set; }
public DateTime StartTime { get; set; }
public string EventName { get; set; }
public TimelineCategoryItem EventCategory { get; set; }
public string EventSubText { get; set; }
}
public static class GlimpseTimeline
{
private static readonly TimelineCategoryItem DefaultCategory = new TimelineCategoryItem( "User", "green", "blue" );
public static OngoingCapture Capture( string eventName )
{
return Capture( eventName, null, DefaultCategory, new TimelineMessage() );
}
public static OngoingCapture Capture( string eventName, string eventSubText )
{
return Capture( eventName, eventSubText, DefaultCategory, new TimelineMessage() );
}
internal static OngoingCapture Capture( string eventName, TimelineCategoryItem category )
{
return Capture( eventName, null, category, new TimelineMessage() );
}
internal static OngoingCapture Capture( string eventName, TimelineCategoryItem category, ITimelineMessage message )
{
return Capture( eventName, null, category, message );
}
internal static OngoingCapture Capture( string eventName, ITimelineMessage message )
{
return Capture( eventName, null, DefaultCategory, message );
}
internal static OngoingCapture Capture( string eventName, string eventSubText, TimelineCategoryItem category, ITimelineMessage message )
{
if (string.IsNullOrEmpty( eventName ))
{
throw new ArgumentNullException( "eventName" );
}
#pragma warning disable 618
var executionTimer = GlimpseConfiguration.GetConfiguredTimerStrategy()();
var messageBroker = GlimpseConfiguration.GetConfiguredMessageBroker();
#pragma warning restore 618
if (executionTimer == null || messageBroker == null)
{
return OngoingCapture.Empty();
}
return new OngoingCapture( executionTimer, messageBroker, eventName, eventSubText, category, message );
}
public static void CaptureMoment( string eventName )
{
CaptureMoment( eventName, null, DefaultCategory, new TimelineMessage() );
}
public static void CaptureMoment( string eventName, string eventSubText )
{
CaptureMoment( eventName, eventSubText, DefaultCategory, new TimelineMessage() );
}
internal static void CaptureMoment( string eventName, TimelineCategoryItem category )
{
CaptureMoment( eventName, null, category, new TimelineMessage() );
}
internal static void CaptureMoment( string eventName, TimelineCategoryItem category, ITimelineMessage message )
{
CaptureMoment( eventName, null, category, message );
}
internal static void CaptureMoment( string eventName, ITimelineMessage message )
{
CaptureMoment( eventName, null, DefaultCategory, message );
}
internal static void CaptureMoment( string eventName, string eventSubText, TimelineCategoryItem category, ITimelineMessage message )
{
if (string.IsNullOrEmpty( eventName ))
{
throw new ArgumentNullException( "eventName" );
}
#pragma warning disable 618
var executionTimer = GlimpseConfiguration.GetConfiguredTimerStrategy()();
var messageBroker = GlimpseConfiguration.GetConfiguredMessageBroker();
#pragma warning restore 618
if (executionTimer == null || messageBroker == null)
{
return;
}
message
.AsTimelineMessage( eventName, category, eventSubText )
.AsTimedMessage( executionTimer.Point() );
messageBroker.Publish( message );
}
public class OngoingCapture : IDisposable
{
public static OngoingCapture Empty()
{
return new NullOngoingCapture();
}
private OngoingCapture()
{
}
public OngoingCapture( IExecutionTimer executionTimer, IMessageBroker messageBroker, string eventName, string eventSubText, TimelineCategoryItem category, ITimelineMessage message )
{
Offset = executionTimer.Start();
ExecutionTimer = executionTimer;
Message = message.AsTimelineMessage( eventName, category, eventSubText );
MessageBroker = messageBroker;
}
private ITimelineMessage Message { get; set; }
private TimeSpan Offset { get; set; }
private IExecutionTimer ExecutionTimer { get; set; }
private IMessageBroker MessageBroker { get; set; }
public virtual void Stop()
{
var timerResult = ExecutionTimer.Stop( Offset );
MessageBroker.Publish( Message.AsTimedMessage( timerResult ) );
}
public void Dispose()
{
Stop();
}
private class NullOngoingCapture : OngoingCapture
{
public override void Stop()
{
}
}
}
}

Any way to not have to retroactively produce these data- attributes?

This Html.DropDownListFor helper should produce the data- attributes but fails to if there is any nesting. So this fails to produce them:
#(
Html.DropDownListFor(
m => m.P[0].CId,
new SelectList(
Model.Cs.Values,
"Id", "DisplayFields", Model.Cs.StartValue),
Model.Cs.Message
)
)
However, this produces them just fine:
#(
Html.DropDownListFor(
m => m.CId,
new SelectList(
Model.Cs.Values,
"Id", "DisplayFields", Model.Cs.StartValue),
Model.Cs.Message
)
)
How can I avoid having to go back and define the missing data- attributes manually with some script?
There is no way but to retroactively assign these missing data- attributes when using the #Html.DropDownListFor helper. The important ones to keep are data-val = "true", data-val-required="This value is required". Furthermore, a span for the validation with data-valmsg-replace="true", data-valmsg-for="ID OF SELECT ELEMENT", class="field-validation-valid".
However, if you choose to use a custom helper then this can be avoided. This can be seen here: http://forums.asp.net/t/1649193.aspx/1/10 where the answer details how to extend the DropDownListFor helper. This is the code they use:
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString DdUovFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList)
{
return DdUovFor(htmlHelper, expression, selectList, null /* optionLabel */, null /* htmlAttributes */);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString DdUovFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, object htmlAttributes)
{
return DdUovFor(htmlHelper, expression, selectList, null /* optionLabel */, new RouteValueDictionary(htmlAttributes));
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString DdUovFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes)
{
return DdUovFor(htmlHelper, expression, selectList, null /* optionLabel */, htmlAttributes);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString DdUovFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel)
{
return DdUovFor(htmlHelper, expression, selectList, optionLabel, null /* htmlAttributes */);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString DdUovFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel, object htmlAttributes)
{
return DdUovFor(htmlHelper, expression, selectList, optionLabel, new RouteValueDictionary(htmlAttributes));
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")]
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString DdUovFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes)
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
IDictionary<string, object> validationAttributes = htmlHelper
.GetUnobtrusiveValidationAttributes(ExpressionHelper.GetExpressionText(expression), metadata);
if (htmlAttributes == null)
htmlAttributes = validationAttributes;
else
htmlAttributes = htmlAttributes.Concat(validationAttributes).ToDictionary(k => k.Key, v => v.Value);
return SelectExtensions.DropDownListFor(htmlHelper, expression, selectList, optionLabel, htmlAttributes);
}

How to add TipAttribute to Model for MVC3

I want to create a new attribute TipAttribute that I can use to display helpful tips to my users.
public class EditPersonModel {
[Display(Name = "Full Name")]
[Tip(Message = "Enter the person's full name.")]
public string Name { get; set; }
}
So from my View I would do
<div class="editor-field">
#Html.InputFor(m => m.Name)
#Html.TipFor(m => m.Name)
</div>
and it would render
<div class="editor-field">
<input id="Name" name="Name" type="text" value="">
<div class="tip">Enter the person's full name.</div>
</div>
I know that I would need to write an Extension method for HtmlHelper, but I have no idea what should go inside!
public static MvcHtmlString TipFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
???
}
I've started looking at the implementation of DisplayAttribute within Mvc3 Source Code, but it's very complex and doesn't look like providing my own custom attribute is very easy/pluggable. Is there a standard MVC3-way to do this?
Thank you!
Assuming your tip attribute was made metadata aware:
public class TipAttribute: Attribute, IMetadataAware
{
public string Message { get; set; }
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues["tip"] = Message;
}
}
you could:
public static class HtmlExtensions
{
public static IHtmlString TipFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression
)
{
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var tip = metaData.AdditionalValues["tip"] as string;
var div = new TagBuilder("div");
div.AddCssClass("tip");
div.SetInnerText(tip ?? string.Empty);
return new HtmlString(div.ToString());
}
}

Evaluate a lambda expression

I have a class
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
}
And I have a lambda expression of the Person type
Expression<Func<TModel, TProperty>> expression
Who contains this value
{model => model.Name}
How can I evaluate that lambda expression against an instance of Person, to extract the Name value attribute?
You can compile the expression into a delegate and pass in a Person object:
Func<Person, string> getName = expression.Compile();
string name = getName(person);
Using Expression trees:
http://msdn.microsoft.com/en-us/library/bb397951.aspx
http://blogs.msdn.com/b/charlie/archive/2008/01/31/expression-tree-basics.aspx

Html inside label using Html helper

How can I add inline html elements inside a label with Html.Label?
Looks like a good scenario for a custom helper:
public static class LabelExtensions
{
public static MvcHtmlString LabelFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> ex,
Func<object, HelperResult> template
)
{
var htmlFieldName = ExpressionHelper.GetExpressionText(ex);
var for = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName);
var label = new TagBuilder("label");
label.Attributes["for"] = TagBuilder.CreateSanitizedId(for);
label.InnerHtml = template(null).ToHtmlString();
return MvcHtmlString.Create(label.ToString());
}
}
and then:
#Html.LabelFor(
x => x.Name,
#<span>Hello World</span>
)
UPDATE:
To achieve what you asked in the comments section you may try the following:
public static class HtmlHelperExtensions
{
public static MvcHtmlString LabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> ex, Func<object, HelperResult> template)
{
var htmlFieldName = ExpressionHelper.GetExpressionText(ex);
var propertyName = htmlFieldName.Split('.').Last();
var label = new TagBuilder("label");
label.Attributes["for"] = TagBuilder.CreateSanitizedId(htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName));
label.InnerHtml = string.Format(
"{0} {1}",
propertyName,
template(null).ToHtmlString()
);
return MvcHtmlString.Create(label.ToString());
}
}
and then:
#Html.LabelFor(
x => x.Name,
#<em>mandatory</em>
)
Rather then writing an extension method you could use the following razor code:
#{ MvcHtmlString label = Html.LabelFor(m => m.ModelProperty, "<span class='cssClass'>Label HTML</span>", new { #class = "clabel"}); }
#Html.Raw(HttpUtility.HtmlDecode(label.ToString()))
It's not as clean but if you need something quick it works.
I borrowed upon Darin's answer, and added to it. I added in capability for Html before the label text and html after the label text. I also added a bunch of overload methods and comments.
I also got some information from this post: How can I override the #Html.LabelFor template?
Hope if helps folks.
namespace System.Web.Mvc.Html
{
public static class LabelExtensions
{
/// <summary>Creates a Label with custom Html before the label text. Only starting Html is provided.</summary>
/// <param name="startHtml">Html to preempt the label text.</param>
/// <returns>MVC Html for the Label</returns>
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Func<object, HelperResult> startHtml)
{
return LabelFor(html, expression, startHtml, null, new RouteValueDictionary("new {}"));
}
/// <summary>Creates a Label with custom Html before the label text. Starting Html and a single Html attribute is provided.</summary>
/// <param name="startHtml">Html to preempt the label text.</param>
/// <param name="htmlAttributes">A single Html attribute to include.</param>
/// <returns>MVC Html for the Label</returns>
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Func<object, HelperResult> startHtml, object htmlAttributes)
{
return LabelFor(html, expression, startHtml, null, new RouteValueDictionary(htmlAttributes));
}
/// <summary>Creates a Label with custom Html before the label text. Starting Html and a collection of Html attributes are provided.</summary>
/// <param name="startHtml">Html to preempt the label text.</param>
/// <param name="htmlAttributes">A collection of Html attributes to include.</param>
/// <returns>MVC Html for the Label</returns>
public static MvcHtmlString LabelFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, Func<object, HelperResult> startHtml, IDictionary<string, object> htmlAttributes)
{
return LabelFor(html, expression, startHtml, null, htmlAttributes);
}
/// <summary>Creates a Label with custom Html before and after the label text. Starting Html and ending Html are provided.</summary>
/// <param name="startHtml">Html to preempt the label text.</param>
/// <param name="endHtml">Html to follow the label text.</param>
/// <returns>MVC Html for the Label</returns>
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Func<object, HelperResult> startHtml, Func<object, HelperResult> endHtml)
{
return LabelFor(html, expression, startHtml, endHtml, new RouteValueDictionary("new {}"));
}
/// <summary>Creates a Label with custom Html before and after the label text. Starting Html, ending Html, and a single Html attribute are provided.</summary>
/// <param name="startHtml">Html to preempt the label text.</param>
/// <param name="endHtml">Html to follow the label text.</param>
/// <param name="htmlAttributes">A single Html attribute to include.</param>
/// <returns>MVC Html for the Label</returns>
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Func<object, HelperResult> startHtml, Func<object, HelperResult> endHtml, object htmlAttributes)
{
return LabelFor(html, expression, startHtml, endHtml, new RouteValueDictionary(htmlAttributes));
}
/// <summary>Creates a Label with custom Html before and after the label text. Starting Html, ending Html, and a collection of Html attributes are provided.</summary>
/// <param name="startHtml">Html to preempt the label text.</param>
/// <param name="endHtml">Html to follow the label text.</param>
/// <param name="htmlAttributes">A collection of Html attributes to include.</param>
/// <returns>MVC Html for the Label</returns>
public static MvcHtmlString LabelFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, Func<object, HelperResult> startHtml, Func<object, HelperResult> endHtml, IDictionary<string, object> htmlAttributes)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
//Use the DisplayName or PropertyName for the metadata if available. Otherwise default to the htmlFieldName provided by the user.
string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(labelText))
{
return MvcHtmlString.Empty;
}
//Create the new label.
TagBuilder tag = new TagBuilder("label");
//Add the specified Html attributes
tag.MergeAttributes(htmlAttributes);
//Specify what property the label is tied to.
tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
//Run through the various iterations of null starting or ending Html text.
if (startHtml == null && endHtml == null) tag.InnerHtml = labelText;
else if (startHtml != null && endHtml == null) tag.InnerHtml = string.Format("{0}{1}", startHtml(null).ToHtmlString(), labelText);
else if (startHtml == null && endHtml != null) tag.InnerHtml = string.Format("{0}{1}", labelText, endHtml(null).ToHtmlString());
else tag.InnerHtml = string.Format("{0}{1}{2}", startHtml(null).ToHtmlString(), labelText, endHtml(null).ToHtmlString());
return MvcHtmlString.Create(tag.ToString());
}
}
}
You will have to write your own helper. The built-in Html.Label helper automatically HTML-encodes the labelText parameter.
In order to meet the SOC and Solid principles, the code can be enhanced to the following code:
public static MvcHtmlString LabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> ex,bool applyStylingHtml)
{
var metadata = ModelMetadata.FromLambdaExpression(ex, htmlHelper.ViewData);
string displayName = metadata.DisplayName;
string description= metadata.Description;
if (String.IsNullOrEmpty(displayName))
{
return MvcHtmlString.Empty;
}
var sb = new StringBuilder();
sb.Append(displayName);
var htmlFieldName = ExpressionHelper.GetExpressionText(ex);
var propertyName = htmlFieldName.Split('.').Last();
var tag = new TagBuilder("label");
tag.Attributes["for"] = TagBuilder.CreateSanitizedId(htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName));
tag.SetInnerText(sb.ToString());
//Func<object, HelperResult> template='<em>';
HtmlString nestedHtml=new HtmlString("<em>"+description+"</em>");
tag.InnerHtml = string.Format(
"{0} {1}",
tag.InnerHtml,
nestedHtml
);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
Then use it in the Razor code:
#Html.LabelFor(m => m.Phone,true)
To make everything more dynamic, description attribute should be applied on the Model class then HtmlHelper will grab the Description as a text to be applied "em" Html tag:
[Display(Name ="Phone",Description = "should be included extention")]
public string Phone { get; set; }
Just a heads up that you need to import the your customized HtmlHelper namespace to the view by adding:
#using yourNamespace

Resources