Your First Server Control
Introduction
Even though ASP.NET provides you with plethora of server controls, sooner or
later you require something that doesn't ship out of the box. Custom server
controls is the way to go in such cases. Custom server controls provide much
more control on the overall control creation process than the web user controls
including:
- Complete control over HTML markup
- Design time support
- Easy reuse across multiple web sites
- Black box deployment
The topic of custom server controls is a vast topic in itself and we kick off
our journey by developing a simple yet useful server control.
Now onwards I use the term custom server control and custom control
interchangeably.
Basics
Custom server controls are the classes that inherit directly or indirectly
from any of the following classes:
- System.Web.UI.Control
- System.Web.UI.WebControl
The Control base class provides bare minimum functionality to your controls
whereas WebControl base class adds support for UI related properties such as
Font, ForeColor and Border.
You can develop a custom server control in two ways:
- By creating a class library type of project
- By adding a class in App_Code folder of your web site
Throughout this book we use the former way since it has its own advantages
(such as toolbox support).
Hyper Link Group Control
To get a feeling of how custom server controls are developed we develop a
HyperLinkGroup control. The HyperLinkGroup control displays a set of hyperlinks
in an HTML table. It loads the links from an XML file and also allows you to
choose the direction of rendering the links, i.e. horizontal or vertical.
Begin your development by creating a new class library project named
HyperLinkGroup. Add two class files - HyperLinkGroup.cs and
HyperLinkGroupDirection.cs. The former class fill contains source code of our
custom control class whereas the later class file defines an
HyperLinkGroupDirection enumeration for representing control rendering
direction.
Now open the HyperLinkGroupDirection.cs class file and add the following code
to it:
namespace BinaryIntellect.UI
{
public enum HyperLinkGroupDirection
{
Horizontal, Vertical
}
}
The above code defines HyperLinkGroupDirection enumeration that is used for
specifying control rendering direction. The HyperLinkGroupDirection enumeration
contains only two values - Horizontal and Vertical. The enumeration is placed
inside BinaryIntellect.UI namespace.
Now open the HyperLinkGroup.cs class file and add the following code to it:
namespace BinaryIntellect.UI
{
public class HyperLinkGroup : Control
{
private string strSource;
public string SourceFile
{
get { return strSource; }
set { strSource = value; }
}
private HyperLinkGroupDirection enumDir;
public HyperLinkGroupDirection Direction
{
get { return enumDir; }
set { enumDir = value; }
}
protected override void Render(HtmlTextWriter writer)
{
DataSet ds = new DataSet();
ds.ReadXml(HttpContext.Current.Server.MapPath(strSource));
if (enumDir == HyperLinkGroupDirection.Horizontal)
{
writer.WriteFullBeginTag("table");
writer.WriteFullBeginTag("tr");
foreach (DataRow row in ds.Tables[0].Rows)
{
writer.WriteFullBeginTag("td");
writer.WriteBeginTag("a");
writer.WriteAttribute("href", row["url"].ToString());
writer.Write(">");
writer.Write(row["title"].ToString());
writer.WriteEndTag("a");
writer.WriteEndTag("td");
}
writer.WriteEndTag("tr");
writer.WriteEndTag("table");
}
else
{
writer.WriteFullBeginTag("table");
foreach (DataRow row in ds.Tables[0].Rows)
{
writer.WriteFullBeginTag("tr");
writer.WriteFullBeginTag("td");
writer.WriteBeginTag("a");
writer.WriteAttribute("href", row["url"].ToString());
writer.Write(">");
writer.Write(row["title"].ToString());
writer.WriteEndTag("a");
writer.WriteEndTag("td");
writer.WriteEndTag("tr");
}
writer.WriteEndTag("table");
}
}
}
}
The above code defines a class (HyperLinkGroup) that inherits from Control
base class. The HyperLinkGroup class contains two public properties viz.
SourceFile and Direction. The SourceFile string property holds the virtual path
of an XML file containing links to render. The Direction property is of
enumerated type HyperLinkGroupDirection and specifies the direction of rendering
for the hyperlinks.
What follows the property definitions is an import part. Our
HyperLinkGroupDirection class overrides Render() method of the Control base
class. The Render() method is used to render the user interface of the custom
control and receives a parameter of type HtmlTextWriter. The HtmlTextWriter
class allows you to emit HTML markup that makes up your control.
The Render() method creates a DataSet and loads the XML file as specified by
the SourceFile property into it using ReadXml() method. I explain the structure
of this XML file later. Note that the ReadXml() method requires physical path of
an XML file whereas the SourceFile property returns a virtual path. The
Server.MapPath() method converts the virtual path into absolute physical path.
Also notice the use of HttpContext class to get a reference of the Server object
inside a class library project.
Depending on the direction of rendering either a table with one row and
multiple cells (horizontal) or a table with one cell and multiple rows
(vertical) is emitted. In any case the code iterates through the Rows collection
of the DataTable and emits the required HTML markup. In order to emit the HTML
markup the code uses HtmlTextWriter object that is received as the method
parameter. The HtmlTextWriter class has several methods that come handy while
rendering the control user interface. The following table lists some of them.
Methods |
Desctiption |
WriteBeginTag() |
Writes the start tag of an HTML element except > character. For
example if used with anchor tag it will emit <a |
WriteFullBeginTag() |
Writes the complete start tag of an HTML element. For example, if
used with anchor tag it will emit <a> |
WriteEndTag() |
Writes the end tag of an HTML element |
WriteAttribute() |
Writes an attribute and its value |
RenderBeginTag() |
Similar to WriteBeginTag() but you can specify the HTML tags via
HtmlTextWriterTag enumeration |
RenderEndTag() |
Renders the end tag of the nearest HTML element that was emitted
using RenderBeginTag() method |
AddAttribute() |
Adds an attribute to the output stream. This attribute is then used
by a tag emitted using RenderBeginTag() method |
AddStyleAttribute() |
Similar to AddAttribute() but adds a style attribute to the output
stream |
In the above code we use only WriteXXXX methods to emit table, rows, cells
and anchor tags. At this stage you may wonder about the title and url columns of
DataRow. It will be clear when we create the XML file.
This completes the HyperLinkGroup control. Make sure to compile the class
library to get its assembly before you proceed further.
Consuming a Custom Control
Once you develop a custom control you can consume it in your web site in two
ways:
- By adding @Register directive manually
- By adding the control to the toolbox
Let's see the first way of consuming a custom control.
Add a new web site to the same solution as that of the class library. Then
add a reference to HyperLinkGroup class library project inside the web site so
that the assembly of the custom control gets copied in the Bin folder.
Now, open the default web form and add the following markup in it:
<%@ Register
Namespace="BinaryIntellect.UI"
Assembly="HyperLinkGroupControl"
TagPrefix="binaryintellect" %>
The @Register directive registers a custom control with the page framework.
It conveys several pieces of information about our control to ASP.NET. The
Namespace attribute specifies the namespace that contains the custom control
class. The Assembly attribute specifies the assembly (.dll) of our custom
control. Finally, the TagPrefix attributes specifies a tag prefix to be used
while instantiating our control in the further markup.
Once you register the HyperLinkGroup control with the page framework, you can
use it on the web form. Add the following markup somewhere between the <form>
and </form> tags.
<binaryintellect:HyperlinkGroup ID="group1" runat="server"
SourceFile="~/links.xml"
Direction="Horizontal">
</binaryintellect:HyperlinkGroup>
You will observe that the public properties of the control appear in the
IntelliSense. You could have also set these properties via Properties window.
Before you run the web form you must create the Links.xml file referenced by the
SourceFile property above. To do so add a new XML file to the web site and add
the following markup to it.
<links>
<link>
<title>Microsoft.com</title>
<url>http://www.microsoft.com</url>
</link>
<link>
<title>Yahoo.com</title>
<url>http://www.yahoo.com</url>
</link>
<link>
<title>Google.com</title>
<url>http://www.google.com</url>
</link>
</links>
The root node of the markup is <links> and it contains multiple <link> nodes.
Each <link> node in turn contains two child nodes - <title> and <url>. Recollect
that in the Render() method we used title and url columns of the DataRow. Those
column names are governed by the tag names of the child nodes of <link> node.
You can use any other name instead of <title> and <url> but make sure to use the
same names in the Render() method.
That's it! Your first custom server control is ready. You can run the default
web form to see how it renders itself at runtime. The following figure shows a
sample run of the web form.

Adding a Custom Control to Visual Studio Toolbox
In the preceding example you manually added the @Register directive and the
control instance to the web form. You can avoid this manual activity by adding
the custom control to the Visual Studio toolbox.
To add a custom control to the toolbox, right click on the toolbox and select
"Choose Items..." menu option. Doing so opens a dialog as shown below:

Then using Browse button select the assembly of the custom control. Your
control appears as checked in the list. You can now close the dialog and simply
drag and drop the control from the toolbox onto the web form.
Visual Studio automates this step also to some extent. However, if you do not
have the source code of the custom control with you then you must follow the
above procedure to add the control onto the toolbox.