Using the Tag Builder
After seeing the last example, some may want a different way to do HtmlHelper extensions and not have to deal with so many angle brackets in code. Fortunately, there is a helpful class that the built-in HtmlHelper extensions use, and that is the TagBuilder class.
As an example, let's say that you want to output the following chunk of Html via an HtmlHelper, where the inner content is an argument of the helper:
<p class="funk">I am a funky paragraph.</p>
If you want to go the plain ol' string route, you could do it like this:
public static string MakeMeAParagraph(
this HtmlHelper helper,
string content)
{
return "" + content + "
";
}
...or even this:
public static string MakeMeAParagraph(
this HtmlHelper helper,
string content)
{
return String.Format("{0}
", content);
}
That works. Alternatively, you could use the TagBuilder:
public static string MakeMeAParagraph(
this HtmlHelper helper,
string content)
{
TagBuilder tb = new TagBuilder("p");
tb.AddCssClass("funk");
tb.SetInnerText(content);
return tb.ToString();
}
What you use is really up to you. For simple scenarios like this one, this allows you to build your Html easily while avoiding ugly string concatenation in code and string formatting. Sweet.
So let's try creating some slightly more complicated Html. Let's put a div around that paragraph tag. Here's how to do it the wrong way:
public static string MakeMeAParagraph(
this HtmlHelper helper,
string content)
{
TagBuilder p = new TagBuilder("p");
p.AddCssClass("funk");
p.SetInnerText(content);
TagBuilder div = new TagBuilder("div");
div.SetInnerText(p.ToString()); //Don't do this.
return div.ToString();
}
You might be tempted to try this (I did) but you would find that when the inner text is set, that text is Html encoded. If you want it to render correctly, you have to do something like this:
public static string MakeMeAParagraph(
this HtmlHelper helper,
string content)
{
TagBuilder p = new TagBuilder("p");
p.AddCssClass("funk");
p.SetInnerText(content);
TagBuilder div = new TagBuilder("div");
div.InnerHtml = p.ToString(); //Use the InnerHtml property.
return div.ToString();
}
Though the api for this class is a bit inconsistent (one is a method and the other is a property), the idea is fairly straightforward. If you want the inside to be treated like text, send in the content via the SetInnerText method. If you want it to be treated like Html, set the InnerHtml property.
Why Use the TagBuilder?
Other than potential readability benefits, the TagBuilder has a number of methods and properties to make the process of building out the Html less painful. A few have been seen above (e.g. AddCssClass(string class), SetInnerText(string text) and InnerHtml). Adding a class via the AddCssClass method is easier than concatenating strings and escaping quotation marks. A handy and helpful method.
There are other methods and properties on the object, but the other one I find myself using frequently is the Attributes collection. A special method exists for adding a class (presumably because having multiple values in that attribute can be considered normal) already, but the Attributes allows you to set a value for any other attribute arbitrarily (e.g. name, id, etc.).
Another bit of flexibility that using that the use of the TagBuilder gives you is that it gives you a nice object that can be passed around and manipulated. If you have a complex extension, passing a string around through various methods is going to leave you with a less-than-optimal Api. Augmenting the string would require knowledge of the internals of the string in all contexts, which would be hard to follow mentally. But the TagBuilder gives you an object with properties and methods you can use to augment the ultimate output of the extension.
To exemplify the easier Api and the use of the Attributes collection, let's look at an example. Let's say you are creating a method that you will use in a HtmlHelper extension that you are creating:
public static string MakeMeACoolParagraph(string cssClass)
{
string para = "<p></p>";
if (!String.IsNullOrEmpty(cssClass))
para = para.Insert(2, String.Format(" class=\"{0}\"", cssClass));
return para;
}
That runs fine. But you know what is better? This:
public static string MakeMeACoolParagraph(string cssClass)
{
string para = "<p></p>";
if (!String.IsNullOrEmpty(cssClass))
para.AddCssClass(cssClass);
return para.ToString();
}
Now if you wanted to add another attribute to the Html, what would you do? Well, you would have to figure out the right place in the string to add it. However, if you are using the TagBuilder, it is this easy:
public static string MakeMeACoolParagraph(string cssClass, string relValue)
{
string para = "<p></p>";
if (!String.IsNullOrEmpty(cssClass))
para.AddCssClass(cssClass);
if (!String.IsNullOrEmpty(relValue))
para.Attributes.Add("rel", relValue);
return para.ToString();
}
As you can see, the TagBuilder is a much better Api for building up Html than the string class.
Negatives of the TagBuilder
First, other than inner text and Html, the TagBuilder has no concept of hierarchy. It might be useful to have some DOM concept built in so a tree could be built then rendered at one time. That being said, I have yet to have a need for this, so you can take this as simply a theoretical flaw for actual application development.
Second, using the TagBuilder is only slightly better in a testing content than just using strings. If you have a complex tree of elements, having something that could be iterated through would be valuable in testing. As it is, if you want to setup an automated test of the output of a ToString'd TagBuilder, you will either need to do some string matching or parse it as Xml and iterate through it that way. So this brings us back to the first negative. For application development the first negative is theoretical in my mind still, but is actually a real roadblock in testing.

