TAKempis Internet Programming Home
Home Uncle Chutney's Journal Information, Tutorials
Sponsored by my good friends of many years, at iLand Internet Solutions
Start Marquee
Stop Marquee

Anatomy of a Server Control
Part 1 - A Simple Control

by Uncle Chutney

Table of Contents:
Introduction: If you haven't read my previous articles1, 2 on Server Controls, you may want to review them, as this article draws and expands on what is explained in them. This is the first of a series of articles which examine the "anatomy" of a Server Control by building one. Actually, we will build several over the course of this series, each building upon the last. The purpose of this series is to further acquaint the reader with the underlying technology of Server Controls, as well as teaching the basics of development of custom Server Controls.

As you may recall from the first article, any class which renders an HTML Interface, and has back-end processing, is derived (inherited) from System.Web.UI.Control. This includes everything from Page to DataGrid. In order to illustrate this, and how Server Controls render HTML to an ASP.Net page, we will build the simplest form of Server Control that there is: A LiteralControl.

Requirements: This demonstration was built with the Microsoft Visual Basic.Net language, using Microsoft's Visual Studio.Net. While these examples could fairly easily be written in Notepad, and converted in order to prevent the necessity of compilation, it is recommended that you use the same tools.

About LiteralControl: If you have programmed with ASP.Net before, you should recognize the name "LiteralControl." The CLR (Common Language Runtime) includes System.Web.UI.LiteralControl, and our LiteralControl will be exactly like it. In fact, I named it with the same name for a couple of reasons. First, it works exactly like the built-in System.Web.UI.LiteralControl. Secondly, I wanted to illustrate that you can have classes with the same name in different namespaces (although this is not generally a good idea).

The System.Web.UI.LiteralControl class renders HTML in an ASPX page... literally. ;-) It is a form of placeholder, which you can insert into an ASPX page, and manipulate its HTML contents programmatically. It inherits System.Web.UI.Control, and therefore has all the properties, methods, and events of all Server Controls. In addition, it has only one other property: Text. the Text property of a LiteralControl is a string, which can be any text.

An interesting fact about the System.Web.UI.LiteralControl is that when an ASPX page is compiled, all raw HTML in the Page template (.aspx) is converted to LiteralControls. This means, for example, that if you were to create an empty WebForm, with only the basic HTML of an HTML document and an empty form, and compiled it, you could see (if you set a Breakpoint in your compiler, and used a Watch to view the Controls Collection of the Page) that there are already three Controls in the Collection:

  1. All HTML from the first "<html>" tag to just above the <form> tag is one LiteralControl
  2. The form itself is a System.Web.UI.HtmlControls.HtmlForm Control
  3. All HTML from just after the </form> tag to the last "</html>" tag is one LiteralControl

Rendering an Interface: The most common thing that any Server Control does is to render an interface, that is, write out HTML to the document. That is why we are beginning with a LiteralControl, because that is all that a LiteralControldoes. In my last article2, I outlined all the shared properties, methods, and events of all Server Controls, and walked you through the LifeCycle of a Server Control. One method mentioned in that LifeCycle is the Render() method. The Render() method is responsible for writing out all the HTML of a Server Control to the output stream of the Page. And, for the LiteralControl, that is the only method we need to be concerned with. A LiteralControl cannot handle server-side events, or maintain state; it just writes HTML to the page.

Building DemoControls.LiteralControl: We have already mentioned that our LiteralControl will have one additional property that is not inherited from System.Web.UI.Control: the Text property. We have also mentioned that we will need to override the Render() method. One last thing will be needed: The System.Web.UI.LiteralControl has an overloaded New() constructor. It can either take no parameters, or you can set the Text property by passing the Text to it. So, we will also need to write 2 versions of the New() constructor.

I began by creating a Visual Studio.Net Project, which was a Class Library. I named the Assembly "DemoControls" and therefore it became the Namespace for all classes contained therein. I then renamed the default class provided to "LiteralControl." I added a reference to System.Drawing in the Project, which is necessary in this case, and in my Class code I imported System.Web.UI. The complete and commented code for this class follows:

Imports System.Web.UI
Imports System.Collections.Specialized

Public Class LiteralControl
     Inherits Control

     ' ** We initialize the _Text property to a blank string, to prevent errors
     ' ** that would occur if the control was used without setting the _Text value first
     Private _Text As String = ""
     Public Property Text() As String
          Get
               Return _Text
          End Get
          Set(ByVal Value As String)
               _Text = Value
          End Set
     End Property

     ' ** The New Sub is Overloaded, so that the Text can be initialized either in the New Sub
     ' ** or in code somewhere
     Public Sub New()
     End Sub

     Public Sub New(ByVal p_strText As String)
          _Text = p_strText
     End Sub

     ' ** The Render Sub simply writes the value out to the output stream
     Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
          writer.Write(_Text)
     End Sub
End Class

Simple, huh? Now to build an ASPX page for my control...

First, I compiled my Assembly, and added the Control to my Toolbox. This is easily done in Visual Studio.Net, by right-clicking the Toolbox, and selecting "Customize..." from the context menu. This brings up a dialog box in which you can browse to the location of the DLL and add it to the Toolbox.

Next, I created a new ASPX page, and added it to my Project. The page consists of 2 sets of code: The Template and the CodeBehind. Using the Designer, I dragged my new LiteralControl onto the page. The IDE named this control "LiteralControl1" and I left it at that. I then went to the Properties Window for this control, and set the Text Property to "Hello World!"

LiteralControl in Visual Studio.Net's IDE

Here is the complete text of the ASPX page as it now stood:

<%@ Register TagPrefix="cc1" Namespace="DemoControls" Assembly="DemoControls" %>
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="Default.aspx.vb" Inherits="DemoWeb.WebForm1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
     <HEAD>
          <title>WebForm1</title>
          <meta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0">
          <meta name="CODE_LANGUAGE" content="Visual Basic 7.0">
          <meta name="vs_defaultClientScript" content="JavaScript">
          <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
     </HEAD>
     <body MS_POSITIONING="GridLayout">
          <form id="Form1" method="post" runat="server">
               <cc1:LiteralControl id="LiteralControl1" runat="server"
              Text="Hello World!"></cc1:LiteralControl>
          </form>
     </body>
</HTML>

I could have stopped there, but for this demonstration I wanted to display the Control in 2 different ways:

  1. Via Template (as above)
  2. Via CodeBehind (dynamically added)

The IDE had already added the CodeBehind for the Control which I dragged onto the grid. Now I was going to add another one in my Page_Load Sub:

Public Class WebForm1
     Inherits System.Web.UI.Page
     Protected WithEvents LiteralControl1 As DemoControls.LiteralControl

     #Region " Web Form Designer Generated Code "

          'This call is required by the Web Form Designer.
          <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

          End Sub

          Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) _
               Handles MyBase.Init
          'CODEGEN: This method call is required by the Web Form Designer
          'Do not modify it using the code editor.
               InitializeComponent()
          End Sub

     #End Region

     Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
          Handles MyBase.Load
          ' ** Here is where I added the second Control:
          Dim objControl As DemoControls.LiteralControl
         objControl = New DemoControls.LiteralControl("<br>Good-bye, World!")
         FindControl("Form1").Controls.Add(objControl)

     End Sub
End Class

A couple of notes: Note that I used FindControl() to locate the Form control in the page. As you should recall, all "raw" HTML in the page is converted at compile-time to LiteralControls. Therefore, "Form1" is not going to be the first Control in the page. We can use the FindControl() method to return it by its ID Property.

After compiling the Page, I took it for a test run in Internet Explorer:

LiteralControl In Action (browser view)

Now let's take a look at the HTML for this page, as it appears in the browser:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
     <HEAD>
          <title>WebForm1</title>
          <meta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0">
          <meta name="CODE_LANGUAGE" content="Visual Basic 7.0">
          <meta name="vs_defaultClientScript" content="JavaScript">
          <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
     </HEAD>
     <body MS_POSITIONING="GridLayout">
          <form name="Form1" method="post" action="Default.aspx" id="Form1">
               <input type="hidden" name="__VIEWSTATE"
               value="dDwxNzk1Mjc1NDk1Ozs+VRIWkLD2syC99Vmp2Z/DFvS1v+o=" />

               Hello World!
               <br>Good-bye, World!</form>
     </body>
</HTML>

Note that the text rendered by the 2 LiteralControls falls between the <form...> and </form> tags. In our Template, the first LiteralControl was inserted inside the form. The second LiteralControl was added to the Controls Collection of the form. From my previous article2, you should remember that adding a Server Control to the Controls Collection of another Server Control results in the HTML for that Server Control being inserted just before the last tag of the Parent Control. In this case, we added the second LiteralControl to the Controls Collection of the form. See how it is rendered just prior to the </form> tag.

Conclusions: By building a LiteralControl, we have demonstrated how a Server Control renders an HTML interface. This is done via the Render() method of the Control. All that this method does is to write HTML to the output stream (inserts it into the HTML text of the document sent to the browser), at the position specified by its' relationship to other Controls in the page. That physical position in the HTML code corresponds to the index of that Control in the Controls Collection of the Parent Control.

 
Footnotes:
1. See "ASP.Net Fundamentals"
2. See "The Life and Times of a Server Control"
Home Uncle Chutney's Journal Information, Tutorials
TAKempis Internet Programming Home
© 1997-2007 by TAKempis - All Rights Reserved