Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Razor base type / Templated Razor Delegates with the "using" keyword

I currently have a div container for all of the input fields in my form, similar to:

<div class="ux-single-field ui-widget-content ui-corner-all">
  @Html.LabelFor(m => m.Name)
  @Html.TextBoxFor(m => m.Name)
</div>

I would like to know how could I encapsulate this using a templated razor delegate (or any other trick), so just like we use:

@using (Html.BeginForm()) {
}

I could simply wrap my elements like:

@using (Html.ContentField()) {
  @Html.LabelFor(m => m.Name)
  @Html.TextBoxFor(m => m.Name)
}
like image 397
rebelliard Avatar asked Apr 13 '11 12:04

rebelliard


2 Answers

Using the Razor View Engine, here is what works:

namespace MyProject.Web.Helpers.Extensions
{
    public static class LayoutExtensions
    {
        public static ContentField BeginContentField(this HtmlHelper htmlHelper)
        {
            return FormHelper(htmlHelper, new RouteValueDictionary());
        }

        public static ContentField BeginContentField(this HtmlHelper htmlHelper, RouteValueDictionary htmlAttributes)
        {
            return FormHelper(htmlHelper, htmlAttributes);
        }

        public static void EndContentField(this HtmlHelper htmlHelper)
        {
            htmlHelper.ViewContext.Writer.Write("</div>");
        }

        private static ContentField FormHelper(this HtmlHelper htmlHelper, IDictionary<string, object> htmlAttributes)
        {
            TagBuilder tagBuilder = new TagBuilder("div");
            tagBuilder.MergeAttributes(htmlAttributes);
            tagBuilder.MergeAttribute("class", "ux-single-field ui-widget-content ui-corner-all");

            htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
            return new ContentField(htmlHelper.ViewContext.Writer);
        }
    }

    public class ContentField : IDisposable
    {
        private bool _disposed;
        private readonly TextWriter _writer;

        public ContentField(TextWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");

            _writer = writer;
        }

        [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
        public void Dispose()
        {
            Dispose(true /* disposing */);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                _disposed = true;

                _writer.Write("</div>");
            }
        }

        public void EndForm()
        {
            Dispose(true);
        }
    }
}

FYI: Using the old ASPX engine, here's how to do it.

like image 101
rebelliard Avatar answered Sep 19 '22 11:09

rebelliard


The accepted answer was very helpful. I changed and updated it a bit for my project, and I feel that this version will be slightly more clear for people who just want to jump in and get things done.

Changes include:

  • Upgraded to use anonymous types instead of IDictionary, since that seems to be the standard now.
  • Removed the Begin/End... syntax, since I will only ever use this with the using() syntax and for that purpose I feel this is more clear.
  • Tweaked naming for clarity.
  • Added a headerText argument, which my panel uses to create a seperate div header. This is easily removed if you don't need/want it.
  • Refactored a few methods out.
  • If you happen to be looking for a panel helper for KendoUI -- well, that happens to be what this is. I have a class called panel that this references, and that just adds margin and width to the KendoUI tags.

    using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Web.Mvc;

    namespace MyProject.Web.HtmlHelpers.Extensions { public static class LayoutExtensions { public static StyledPanel Panel(this HtmlHelper htmlHelper, object htmlAttributes = null, string headerText = null) { return GetStyledPanel(htmlHelper, headerText, htmlAttributes); }

        private static StyledPanel GetStyledPanel(this HtmlHelper htmlHelper, string headerText, object htmlAttributes)
        {
    
            if (!string.IsNullOrWhiteSpace(headerText))
                RenderHeading(htmlHelper, headerText);
    
            RenderDiv(htmlHelper, htmlAttributes);
    
            return new StyledPanel(htmlHelper.ViewContext.Writer);
        }
    
        private static void RenderHeading(HtmlHelper htmlHelper, string headerText)
        {
            TagBuilder tagBuilder = new TagBuilder("div");
            tagBuilder.Attributes.Add("class", "panelHead");
            tagBuilder.SetInnerText(headerText);
    
            htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.Normal));
        }
    
        private static void RenderDiv(HtmlHelper htmlHelper, object htmlAttributes) 
        {
            TagBuilder Tag = new TagBuilder("div");
            Tag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
            Tag.MergeAttribute("class", "panel k-block k-shadow");
    
            htmlHelper.ViewContext.Writer.Write(Tag.ToString(TagRenderMode.StartTag));        
        }
    
    }
    
    public class StyledPanel : IDisposable
    {
        private bool m_Disposed;
        private readonly TextWriter m_Writer;
    
        public StyledPanel(TextWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException("Writer was null. This should never happen.");
    
            m_Writer = writer;
        }
    
        [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (!m_Disposed)
            {
                m_Disposed = true;
                m_Writer.Write("</div>");
            }
        }
    
        public void EndForm()
        {
            Dispose(true);
        }
    }
    

    }

Usage could be like this:

@using (Html.Panel(headerText: "My Header", 
                   htmlAttributes: new { style = "width: 800px;" }))
{ 
    <table>
            <tr>
                <td class="label">First Name:</td>
                <td class="content"><input name="thing" class="k-textbox" /></td>

                <td class="label">Last Name:</td>
                <td class="content"><input name="thing" class="k-textbox" /></td>
            </tr>
    </table>
}

Or just:

@using (Html.Panel())
{ 
    <table>
            <tr>
                <td class="label">First Name:</td>
                <td class="content"><input name="thing" class="k-textbox" /></td>

                <td class="label">Last Name:</td>
                <td class="content"><input name="thing" class="k-textbox" /></td>
            </tr>
    </table>
}
like image 22
Brian MacKay Avatar answered Sep 18 '22 11:09

Brian MacKay