Dot Net Perls
ASP.NET

HtmlTextWriter Use

by Sam Allen

Problem

Write HTML programmatically using the HtmlTextWriter object. For example, we may want to generate a list of <div> elements. We could use StringBuilder to generate the HTML, but we want more robust and understandable code. We should learn about and master the HtmlTextWriter class.

Solution: C#

Here's a high-level synopsis here first. HTML is a string and it is natural and easy to generate HTML using StringBuilder objects in C#. That method is also prone to many bugs and extremely complex syntax. Right here I will present an example solution that uses HtmlTextWriter, and then reveal the version with StringBuilder.

/// <summary>
/// Generate the HTML markup I can put to show teaser banners in a page.
/// </summary>
public string GetTeaserMarkup()
{
    StringWriter stringWriter = new StringWriter();

    // Put HtmlTextWriter in using block because it needs to call Dispose.
    using (HtmlTextWriter writer = new HtmlTextWriter(stringWriter))
    {
        foreach (SpecialObject item in _teaserItems)
        {
            // Some strings for the attributes.
            string classString = "ClassName";
            string realUrl = "http://dotnetperls.com/";
            string imgStr = "image.jpg";
            string boldText = "Some text";
            // [code omitted]

            // The important part.
            writer.AddAttribute(HtmlTextWriterAttribute.Class, classString);
            writer.RenderBeginTag(HtmlTextWriterTag.Div); // Begin #1

            writer.AddAttribute(HtmlTextWriterAttribute.Href, realUrl);
            writer.RenderBeginTag(HtmlTextWriterTag.A); // Begin #2

            writer.AddAttribute(HtmlTextWriterAttribute.Src, imgSrc);
            writer.AddAttribute(HtmlTextWriterAttribute.Width, "60");
            writer.AddAttribute(HtmlTextWriterAttribute.Height, "60");
            writer.AddAttribute(HtmlTextWriterAttribute.Alt, "");

            writer.RenderBeginTag(HtmlTextWriterTag.Img); // Begin #3
            writer.RenderEndTag(); // End #3

            writer.Write(boldText);

            writer.RenderEndTag(); // End #2
            writer.RenderEndTag(); // End #1
        }
    }
    return stringWriter.ToString();
}

Make tags with HtmlTextWriter

You may recognize the Push and Pop methods on queues. Call RenderBeginTag to open a new level in the HTML with that tag. You must manually close that level of that markup with the RenderEndTag method call. RenderBeginTag and RenderEndTag must be in pairs! Let me repeat the above code.

// The important part (repeated from above).
writer.AddAttribute(HtmlTextWriterAttribute.Class, classString);
writer.RenderBeginTag(HtmlTextWriterTag.Div); // Begin #1

writer.AddAttribute(HtmlTextWriterAttribute.Href, realUrl);
writer.RenderBeginTag(HtmlTextWriterTag.A); // Begin #2

writer.AddAttribute(HtmlTextWriterAttribute.Src, imgSrc);
writer.AddAttribute(HtmlTextWriterAttribute.Width, "60");
writer.AddAttribute(HtmlTextWriterAttribute.Height, "60");
writer.AddAttribute(HtmlTextWriterAttribute.Alt, "");

writer.RenderBeginTag(HtmlTextWriterTag.Img); // Begin #3
writer.RenderEndTag(); // End #3

writer.Write(boldText);

writer.RenderEndTag(); // End #2
writer.RenderEndTag(); // End #1

Example output

The next code block here shows markup similar to what the above code might produce in a foreach loop. The spacing can be adjusted to anything you want, as I show in the following part of this document.

<div class="ClassName">
    <a href="page1.aspx"><img src="image1.jpg" [...] />Inner text.</a>
</div><div class="ClassName">
    <a href="page2.aspx"><img src="image2.jpg"       />More inner text.</a>
</div><div class="ClassName">
    <a href="page3.aspx"><img src="image3.jpg"       />Are you reading this?</a>
</div>

Tabstring parameter reduces whitespace

In the constructor to the HtmlTextWriter, use an empty string (string.Empty) as the second parameter. This will eliminate a lot of whitespace, and I was able to reduce the size of my pages by a (small) percentage this way.

using (HtmlTextWriter writer = new HtmlTextWriter(stringWriter, string.Empty))
{
    // etc.
}

Conclusion

Use HtmlTextWriter to write HTML instead of StringBuilder alone for the cleanest and object-oriented code. I am not aware of any performance issues, and the markup is always nicely indented. For XML, use XmlWriter, which is a very similar class, or even the XElement class in LINQ.

© 2008 Sam Allen. All rights reserved.

Ads by The Lounge