Use of the HtmlTextWriter Class to Render Custom Controls in VB.NET

This article will address the use of the HtmlTextWriter class and the role is plays in the construction of custom server controls. As custom server control development is accomplished without a visual designer, the HtmlTextWriter class provides a mechanism for precisely defining the output of the custom control directly within the code and in absence of the designer.

Introduction :

This article will address the use of the HtmlTextWriter class and the role is plays in the construction of custom server controls. As custom server control development is accomplished without a visual designer, the HtmlTextWriter class provides a mechanism for precisely defining the output of the custom control directly within the code and in absence of the designer.

Whilst the HtmlTextWriter class has quite a few moving parts, this discussion will focus upon a few of the more common and frequently used parts of the class and will demonstrate how they may be used to render custom controls. This is not a complete or thorough treatment of the topic but rather just enough information to get someone started with no prior experience with the class.

Purpose

For the purposes of this discussion, the HtmlTextWriter class is used to output properly formed HTML at the insertion point of the custom control into a web page. The HtmlTextWriter allows the developer to code attributes, style attributes, and tags as well as providing the means to render subordinate controls within a custom server or composite control.

As far as usage goes, controls are generally rendered by overriding the RenderContents subroutine; this article addresses the use of the HtmlTextWriter class as it may be used to render a control in the overridden RenderContents subroutine. This article does not address all aspects of custom control development; it is strictly limited to the basics associated with the HtmlTextWriter class.

HtmlTextWriter Class

The HtmlTextWriter class permits the developer to generate page output in a non-browser specific way; that is to say, the burden of developing alternative markup is typically removed by using the HtmlTextWriter. The HtmlTextWriter class exposes properties, fields, and methods that permit the developer to format HTML 4.0 compliant output. There is also an XhtmlTextWriter class that performs a similar function for XHTML as used with different device types but this discussion will be limited to the HtmlTextWriter although what is said of one generally applies to other with the obvious differences in the markup.

In the last paragraph I'd stated that the HtmlTextWriter typically relieves the developer of the burden of rendering alternative markup. The reason I used the word typically is because, like most good things, the HtmlTextWriter class can be somewhat abused as a function of its flexibility. This misuse of the class usually occurs through the Write method which will render out just about anything into the HTML generated for the page.

Because of this feature, the developer can circumvent the intent of the class and write tags and content out directly to the page at the insertion point. Right, wrong, it does not matter, if it is wrapped up in a string and submitted to the Write method, it is headed for the page source. There are several issues with this but the most important is likely to be the fact that using this approach offers no real mechanism for catching an error before rendering out the page.

For example, one could code something like this:

output.Write("<table><tr>")

output.Write("<td>Name: </td><td>Frank</td></tr>")

output.Write("<tr>")

output.Write("<td>Name: </td><td>Jessie</td></tr>")

output.Write("<tr>")

output.Write("<td>Name: </td><td>Wyatt</td></tr>")

output.Write("</table>")

Which will result in the display of this table:

HtmlTextWriter1-in-vb.net.png

As you can see, all that was done was to place HTML into a string and pass that string directly output's Write method. Output is an instance of the HtmlTextWriter. Since this example is quite simple it renders out onto the page just fine and will render in Firefox or IE7 without any issues. The real problem occurs with added complexity and the increased potential for errors. If I introduce a single line error as so:

output.Write("<teble><tr>")

What happens is this:

HtmlTextWriter2-in-vb.net.png

As can be seen in the screen shot, the result is no error, no warning, no table. The markup supplied to the page is as follows:

<span id="WebCustomControl1_1"><teble><tr><td>Name: </td><td>Frank</td></tr><tr><td>Name: </td><td>Jessie</td></tr><tr><td>Name: </td><td>Wyatt</td></tr></table></span>

In contrast, to render out the same table using the HtmlTextWriter as I believe it was intended to be used results in this code:

'start the table

output.RenderBeginTag(HtmlTextWriterTag.Table)

 

'start the row

output.RenderBeginTag(HtmlTextWriterTag.Tr)

 

'add data

output.RenderBeginTag(HtmlTextWriterTag.Td)

output.Write("Name: ")

output.RenderEndTag()

output.RenderBeginTag(HtmlTextWriterTag.Td)

output.Write("Freddie")

output.RenderEndTag()

 

'end the row

output.RenderEndTag()

 

'start the row

output.RenderBeginTag(HtmlTextWriterTag.Tr)

 

'add data

output.RenderBeginTag(HtmlTextWriterTag.Td)

output.Write("Name: ")

output.RenderEndTag()

output.RenderBeginTag(HtmlTextWriterTag.Td)

output.Write("Jason")

output.RenderEndTag()

 

'end the row

output.RenderEndTag()

 

'start the row

output.RenderBeginTag(HtmlTextWriterTag.Tr)

 

'add data

output.RenderBeginTag(HtmlTextWriterTag.Td)

output.Write("Name: ")

output.RenderEndTag()

output.RenderBeginTag(HtmlTextWriterTag.Td)

output.Write("Leatherface")

output.RenderEndTag()

 

'end the row

output.RenderEndTag()

 

'end the table

output.RenderEndTag()

As you can see, there are more lines of code associated with this type of use of the class, however, it is safer. In fact, if you were to try to introduce the same error, you'd find that you will not get very far
 

HtmlTextWriter3-in-vb.net.png

 

In the image above, you see that the error is highlighted and this error will prevent building the control as indicated in the next figure.
 

HtmlTextWriter4-in-vb.net.png

 

It may also be interesting to note that the source for the page is more properly formed without any additional effort:

<span id="WebCustomControl1_1"><table>

<tr>

<td>Name: </td><td>Freddie</td>

</tr><tr>

<td>Name: </td><td>Jason</td>

</tr><tr>

<td>Name: </td><td>Leatherface</td>

</tr>

</table></span>

 

Note that the web control is placed into a span as is the default behavior. The beginning tag can be overridden in the control by overriding a read only property called TagKey. After overriding the TagKey property with a table tag, the page source will be as follows: (Note the span is gone and the control is started with the table tag)

<table id="WebCustomControl1_1">

<tr>

<td>Name: </td><td>Freddie</td>

</tr><tr>

<td>Name: </td><td>Jason</td>

</tr><tr>

<td>Name: </td><td>Leatherface</td>

</tr>

</table>

 

The code to override the TagKey to use the table tag is as follows:

 

Protected Overrides ReadOnly Property TagKey() As System.Web.UI.HtmlTextWriterTag

Get

Return HtmlTextWriterTag.Table

End Get

End Property

 

Of course after making this change, the code used to render the control should be modified by dropping the table related beginning and ending tags and just starting off with the first row tag. If you overrode the TagKey property with a div tag, you would still need to add the beginning and ending table tags. The page source for that would end up looking like this:

 

<div>

<div id="WebCustomControl1_1">

<table>

<tr>

<td>Name: </td><td>Freddie</td>

</tr><tr>

<td>Name: </td><td>Jason</td>

</tr><tr>

<td>Name: </td><td>Leatherface</td>

</tr>

</table>

</div>

</div>

 

Adding Attributes

 

It is possible to add attributes to the control's HTML through the use of the HtmlTextWriter class. Attributes added with the HtmlTextWriter need to be added in advance of a beginning tag. For example:

 

'start the table

output.AddAttribute(HtmlTextWriterAttribute.Border, "2")

output.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "3")

output.RenderBeginTag(HtmlTextWriterTag.Table)

 

'start the row

output.RenderBeginTag(HtmlTextWriterTag.Tr)

 

'add data

output.AddAttribute(HtmlTextWriterAttribute.Align, "left")

output.RenderBeginTag(HtmlTextWriterTag.Td)

output.Write("Name: ")

output.RenderEndTag() ...

 

In looking at the example note that, prior to the beginning table tag, the HtmlTextWriter's AddAttribute method was called. AddAttribute is looking for an HtmlTextWriterAttribute tag and a value to associate with the tag. The example passes a tag (e.g., HtmlTextWriterAttribute.Border) and a value for the tag ("2"). It is possible to create an error here, for example, one could to pass value of "A" in lieu of "2" to the border.

 

Adding Styles

 

As with attributes, styles may be added using the HtmlTextWriter class, and also as with attributes, the style attributes need to be added in advance of the beginning tag for the targeted item. Style attributes are added with the HtmlTextWriter's AddStyleAttribute method. This method accepts two arguments as did the AddAttribute method. The first argument is the style and the second argument is the value.

 

In the example below, the table has the font size set to "Large" and the width of the table is set to 100%.

 

'start the table

output.AddStyleAttribute(HtmlTextWriterStyle.FontSize, "Large")

output.AddStyleAttribute(HtmlTextWriterStyle.Width, "100%")

output.AddAttribute(HtmlTextWriterAttribute.Alt, "Its a table")

output.AddAttribute(HtmlTextWriterAttribute.Border, "2")

output.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "3")

output.RenderBeginTag(HtmlTextWriterTag.Table)

 

'start the row

output.RenderBeginTag(HtmlTextWriterTag.Tr)

 

The page source shows the style attributes added to the table:

 

<div id="WebCustomControl1_1">

<table alt="Its a table" border="2" cellpadding="3" style="font-size:Large;width:100%;">

<tr>

<td align="left">Name: </td><td align="left">Freddie</td>

</tr><tr>

<td>Name: </td><td>Jason</td>

</tr><tr>

<td>Name: </td><td>Leatherface</td>

</tr>

</table>

</div>

 

Adding Controls

 

The HtmlTextWriter may also be used to add subordinate controls to the custom control. The following examples show adding different objects to the custom control through the use of object's RenderControl method.

 

Adding an image:

 

output.AddAttribute(HtmlTextWriterAttribute.Align, "center")

output.RenderBeginTag(HtmlTextWriterTag.Tr)

output.RenderBeginTag(HtmlTextWriterTag.Td)

Dim img As New Image()

img.ImageUrl = SourceImg.ToString()

img.BorderStyle = WebControls.BorderStyle.Inset

img.BorderWidth = 2

img.RenderControl(output)

output.RenderEndTag()

output.RenderEndTag()

 

Adding a text box:

 

output.RenderBeginTag(HtmlTextWriterTag.Td)

output.AddStyleAttribute(HtmlTextWriterStyle.FontFamily, Me.Font.Name)

output.AddAttribute(HtmlTextWriterAttribute.Size, _

Me.Font.Size.ToString())

txtAmount.RenderControl(output)

output.RenderEndTag()

 

Adding a button:

 

output.RenderBeginTag(HtmlTextWriterTag.Td)

output.AddStyleAttribute(HtmlTextWriterStyle.FontFamily, Me.Font.Name)

output.AddAttribute(HtmlTextWriterAttribute.Size, _

Me.Font.Size.ToString())

btnSubmit.RenderControl(output)

output.RenderEndTag()


With the exception of the image in the first example, each of the objects would likely be instantiated in the overridden CreateChildControls subroutine; from there the control could have its properties set and could have event handlers added to the instance. The controls are then also added to the control collection. In this case, the RenderContents method merely places the existing controls. Having said that, the image example provided first shows the creation of the image within the RenderContents subroutine.

 

The following example illustrates adding a creating button in the CreateChildControls subroutine; in the example, the button is created, its text property is set, and the click event handler is added to the button; the button is then added to the control collection:

 

' creats the submit button and assigns it a handler

btnSubmit = New Button

btnSubmit.Text = "Submit"

AddHandler btnSubmit.Click, AddressOf btnSubmit_Click

Me.Controls.Add(btnSubmit)

 

Using the same approach indicated in the examples, one could add other types of subordinate controls to the custom control.


Summary

 

This article was intended to provide a brief introduction to the HtmlTextWriter class in the context of using it to render custom controls. This is not an all encompassing description of the class and it does define all of the things that you can through the class. The intent was to provide a sufficient introduction to the topic to allow one to use the class to define an interface for a custom control.