Vadzim Savenok Vadzim Savenok - 7 months ago 65
CSS Question

C# html-helper extend existing method without overriding?

I have already searched for the question and found possible answer, but I still need some help.

I am trying to write an html-helper to extend functionality of already existing LabelFor method

public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes)
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
//string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
var labelText = html.LabelFor(expression);
if (String.IsNullOrEmpty(labelText.ToString()))
return MvcHtmlString.Empty;

if (metadata.IsRequired)
labelText = new MvcHtmlString(labelText.ToString().Substring(0, labelText.ToString().Length - 8).Trim() +
"<span style=\"color:red\" class=\"required-marker\">*</span></label>");
TagBuilder tag = new TagBuilder("label");
tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));

I am trying to add a functionality, where Method would check if variable has a "required" flag, and then performs something (adds red * at the end of label in that case)

[Display(Name = "Year")]
public string ProjectYr { get; set; }

However, I feel like I am overwriting the entire LabelFor functionality. Is there a way to simply add new functionality to existing LabelFor method while preserving all functions of the original without overriding it? Override doesn't work anyway since my method is static.

Thank you very much in advance!


Here's a complete example that does what your asking

Accessing the model attributes in a helper extension class

public static class LabelExtensions
    public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html,
        Expression<Func<TModel, TValue>> expression, IDictionary<String, Object> htmlAttributes,
        String requiredMarker = "*")
        return LabelHelper(html, ModelMetadata.FromLambdaExpression(expression, html.ViewData),
            ExpressionHelper.GetExpressionText(expression), null, htmlAttributes, requiredMarker);

    public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html,
        Expression<Func<TModel, TValue>> expression, Object htmlAttributes, String requiredMarker)
        return LabelFor(html, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes), requiredMarker);

    internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, String htmlFieldName,
        String labelText = null, IDictionary<String, Object> htmlAttributes = null, String requiredMarker = null)
        var resolvedLabelText = labelText ??
                                metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();

        var tag = new TagBuilder("label");
        tag.MergeAttributes(htmlAttributes, true);

        if (metadata.IsRequired && !String.IsNullOrWhiteSpace(requiredMarker))
            var requiredSpan = new TagBuilder("span") {InnerHtml = requiredMarker};

            tag.InnerHtml += requiredSpan;

        var result = tag.ToString(TagRenderMode.Normal);

        return new MvcHtmlString(result);

and here's the unit tests

public static class LabelExtensionFixtures
    public class should_return_label_with_required_info : MvcExtensionFixtureBase
        private class TestClass
            public Guid Id { get; set; }

        private MvcHtmlString _expectedResult;
        private HtmlHelper<TestClass> _sut;
        private MvcHtmlString _result;

        public void Given()
            _expectedResult =
                    "<label class=\"control-label col-md-2\" for=\"Id\">Id<span class=\"required\">*</span></label>");
            _sut = CreateHtmlHelper(new TestClass {Id = Guid.NewGuid()});

            _result = _sut.LabelFor(model => model.Id, new { @class = "control-label col-md-2" }, "*");

        public void Test()
            Assert.That(_result.ToHtmlString(), Is.EqualTo(_expectedResult.ToHtmlString()));

public abstract class MvcExtensionFixtureBase
    protected HtmlHelper<T> CreateHtmlHelper<T>(T instance)
        var viewDataDictionary = new ViewDataDictionary<T>(instance);
        var viewContext = A.Fake<ViewContext>();
        A.CallTo(() => viewContext.ViewData).Returns(viewDataDictionary);

        var viewDataContainer = A.Fake<IViewDataContainer>();
        A.CallTo(() => viewDataContainer.ViewData).Returns(viewDataDictionary);

        return new HtmlHelper<T>(viewContext, viewDataContainer);