Formatting Html Output with HtmlTextWriter
Denise Wynn
May 17, 2005
In the previous article we had just started touching on what the HtmlTextWriter class is capable of doing. This article will show you some of the formatting options available in HtmlTextWriter. To demonstrate these options we'll be modifying the image control from the previous article.
The ASP.NET HyperLink control has an imageurl attribute that allows you to specify an image you would like to use as a link. Unfortunately it suffers the same output problem discussed in the previous article and it's extremely limiting because you can only specify the location of the image. We will solve these problems by extending our new image control. We are going to add a navigateurl attribute but since this is an image control the focus is going to be on the "img" tag instead of on the "a" or anchor tag.
Getting Started
We're going to start with the image control from the previous article and build from there. The code was fairly short and simple - the control had 3 properties or attributes you could set and it output an image tag with appropriate code.
<%@ Control runat="server" language="c#" %>
<script runat="server" language="c#">
private string alt = null;
private string src = null;
private string dtd = "html";
public string AlternateText
{
get{ return alt;}
set{ alt = value;}
}
public string ImageUrl
{
get{ return src;}
set{ src = value;}
}
public string Dtd
{
get{ return dtd;}
set
{
//if the value isn't html or xhtml then we don't want it
if (value.ToLower() == "html" | value.ToLower() == "xhtml")
{
dtd = value.ToLower();
}
}
}
protected override void Render(HtmlTextWriter writer)
{
//this will output the start of the img element - <img
writer.WriteBeginTag("img");
//this will output the src and alt attributes
writer.WriteAttribute("src", src);
writer.WriteAttribute("alt", alt);
//this will output the end of the img element based on dtd
if (dtd == "xhtml")
{
//if it's xhtml we want a self closing end tag "/>"
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
}
else
{
//if it's html we only want to output a right-angle bracket ">"
writer.Write(HtmlTextWriter.TagRightChar);
}
}
</script>
Adding the NavigateUrl property
The first thing we need to do is add the option for the user to specify a url. We've added a private field called href and a public property called NavigateUrl. This will allow the user to specify a url for the link that contains the image.
<%@ Control runat="server" language="c#" %>
<script runat="server" language="c#">
private string alt = null;
private string src = null;
private string dtd = "html";
private string href = null;
public string AlternateText
{
get{ return alt;}
set{ alt = value;}
}
public string ImageUrl
{
get{ return src;}
set{ src = value;}
}
public string Dtd
{
get{ return dtd;}
set
{
//if the value isn't html or xhtml then we don't want it
if (value.ToLower() == "html" | value.ToLower() == "xhtml")
{
dtd = value.ToLower();
}
}
}
public string NavigateUrl
{
get{ return href; }
set{ href = value; }
}
protected override void Render(HtmlTextWriter writer)
{
//this will output the start of the img element - <img
writer.WriteBeginTag("img");
//this will output the src and alt attributes
writer.WriteAttribute("src", src);
writer.WriteAttribute("alt", alt);
//this will output the end of the img element based on dtd
if (dtd == "xhtml")
{
//if it's xhtml we want a self closing end tag "/>"
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
}
else
{
//if it's html we only want to output a right-angle bracket ">"
writer.Write(HtmlTextWriter.TagRightChar);
}
}
</script>
Adding the Rendering Methods
Now we need to do something with our new property. We're going to modify the overridden Render method and add two new methods; RenderAnchor and RenderImage.
<%@ Control runat="server" language="c#" %>
<script runat="server" language="c#">
private string alt = null;
private string src = null;
private string dtd = "html";
private string href = null;
public string AlternateText
{
get{ return alt;}
set{ alt = value;}
}
public string ImageUrl
{
get{ return src;}
set{ src = value;}
}
public string Dtd
{
get{ return dtd;}
set
{
//if the value isn't html or xhtml then we don't want it
if (value.ToLower() == "html" | value.ToLower() == "xhtml")
{
dtd = value.ToLower();
}
}
}
public string NavigateUrl
{
get{ return href; }
set{ href = value; }
}
protected override void Render(HtmlTextWriter writer)
{
if( href != null )
{
RenderAnchor(writer);
}
else
{
//if there isn't a url then we don't want to create
//the anchor tag - just the img tag
RenderImage(writer);
}
}
private void RenderAnchor(HtmlTextWriter writer)
{
writer.WriteBeginTag("a");
writer.WriteAttribute("href", href);
writer.Write(HtmlTextWriter.TagRightChar);
//this is where the formatting comes in
//move the cursor to the next line and indent
writer.WriteLine();
writer.Indent++;
RenderImage(writer);
//move the cursor to the next line and undo the previous indent
writer.WriteLine();
writer.Indent--;
writer.WriteEndTag("a");
}
private void RenderImage(HtmlTextWriter writer)
{
//this will output the start of the img element - <img
writer.WriteBeginTag("img");
//this will output the src and alt attributes
writer.WriteAttribute("src", src);
writer.WriteAttribute("alt", alt);
//this will output the end of the img element based on dtd
if (dtd == "xhtml")
{
//if it's xhtml we want a self closing end tag "/>"
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
}
else
{
//if it's html we only want to output a right-angle bracket ">"
writer.Write(HtmlTextWriter.TagRightChar);
}
}
</script>
Adding two methods instead of one allows us to render the image tag independently from the anchor tag. If there is no url set in NavigateUrl then we can just render a stand alone image without error. The code that was previously handled by the Render method was moved to the new RenderImage method. Now the Render method is just used to determine if we should call RenderAnchor, which then calls RenderImage, or just go to RenderImage without writing an anchor tag.
The HtmlTextWriter Formatting
The use of HtmlTextWriter formatting is located in the RenderAnchor method. By using combinations of the HtmlTextWriter.WriteLine method and HtmlTextWriter.Indent property you can determine what the formatting of the html output will look like.
private void RenderAnchor(HtmlTextWriter writer)
{
writer.WriteBeginTag("a");
writer.WriteAttribute("href", href);
writer.Write(HtmlTextWriter.TagRightChar);
//this is where the formatting comes in
//move the cursor to the next line and indent
writer.WriteLine();
writer.Indent++;
RenderImage(writer);
//move the cursor to the next line and undo the previous indent
writer.WriteLine();
writer.Indent--;
writer.WriteEndTag("a");
}
By adding these formatting options we change the output from:
<a href="myurl"><img src="myimage.jpg" /></a>
to the following, which is more readable when viewing html source code.
<a href="myurl"> <img src="myimage.jpg" /> </a>
In Conclusion
This article showed how you can format the html that your control outputs when using HtmlTextWriter. This allows you to render more readable source html.
For more information on the HtmlTextWriter class check out the official HtmlTextWriter documentation on MSDN.
