Search code examples
asp.net-mvclabelresponse.write

Implement .net mvc BeginLabel like BeginForm response.write issue


I have a requirement to roll my own BeginLabel helper for Mvc. I checked/stole the concept from the Mvc source for the html.beginForm / ajax.beginForm methods.

public static Label BeginLabel(this HtmlHelper htmlHelper)
{
    TagBuilder tagBuilder = new TagBuilder("label");
    HttpResponseBase response = htmlHelper.ViewContext.HttpContext.Response;
    response.Write(tagBuilder.ToString(TagRenderMode.StartTag));
    return new Label(response);
}

The Label simply implements IDisposable interface to enable closing off the label:

protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        _disposed = true;
        _httpResponse.Write("</label>");
    }
}

Usage looks like this:

@using (Html.BeginLabel())
{
    @Html.TextBoxFor(f => f.FirstName)
    @Html.ValidationMessageFor(f => f.FirstName)
}

It looks like i'm missing something as the labels always get rendered at the top of the html and while this is obvious to me because i'm writing to the response, I can't see how the native BeginForm() is achieving this. Can anyone shed any light on this?


Solution

  • public class MvcLabel : IDisposable
    {
        // Fields
        private bool _disposed;
        private readonly TextWriter _writer;
    
        public MvcLabel(ViewContext viewContext)
        {
            if (viewContext == null)
            {
                throw new ArgumentNullException("viewContext");
            }
            this._writer = viewContext.Writer;
        }
    
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                this._disposed = true;
                this._writer.Write("</label>");
            }
        }
    
        public void EndLabel()
        {
            this.Dispose(true);
        }
    }
    

    and

    public static class HtmlHelperExtension
    {
        // Methods
        public static MvcLabel BeginLabel(this HtmlHelper html, string expression)
        {
            return html.BeginLabel(expression, null);
        }
    
        public static MvcLabel BeginLabel(this HtmlHelper html, string expression, string labelText)
        {
            return LabelHelper(html, ModelMetadata.FromStringExpression(expression, html.ViewData), expression, labelText);
        }
    
        public static MvcLabel BeginLabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
        {
            return html.BeginLabelFor<TModel, TValue>(expression, null);
        }
    
        public static MvcLabel BeginLabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText)
        {
            return LabelHelper(html, ModelMetadata.FromLambdaExpression<TModel, TValue>(expression, html.ViewData), ExpressionHelper.GetExpressionText(expression), labelText);
        }
    
        public static MvcLabel BeginLabelForModel(this HtmlHelper html)
        {
            return html.BeginLabelForModel(null);
        }
    
        public static MvcLabel BeginLabelForModel(this HtmlHelper html, string labelText)
        {
            return LabelHelper(html, html.ViewData.ModelMetadata, string.Empty, labelText);
        }
    
        public static void EndLabel(this HtmlHelper htmlHelper)
        {
            htmlHelper.ViewContext.Writer.Write("</label>");
        }
    
        internal static MvcLabel LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string labelText = null)
        {
            string str = labelText ?? (metadata.DisplayName ?? (metadata.PropertyName ?? htmlFieldName.Split(new char[] { '.' }).Last<string>()));
    
            TagBuilder tagBuilder = new TagBuilder("label");
            tagBuilder.Attributes.Add("for", TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName)));
    
            html.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
    
            if (!string.IsNullOrEmpty(str))
            {
                tagBuilder = new TagBuilder("span");
                tagBuilder.SetInnerText(str);
                html.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.Normal));
            }
    
            return new MvcLabel(html.ViewContext);
        }
    }
    

    Hope i can help others...