Learn ASP.NET Web Forms and ASP.NET MVC. Intensive courses for professional developers. Conducted by Bipin Joshi in Thane. Read more details here.

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.


Bipin Joshi is the founder of BinaryIntellect Consulting and conducts professional training programs on ASP.NET in Thane. He is a published author and has authored or co-authored books for Apress and Wrox press. To know more about him click here. To know more about his training programs go here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Associated Links
Download Source Code

Tags : ASP.NET Custom Controls
Posted On : 01 Nov 2007

This page is protected by copyright laws. Copying in any form is strictly prohibited. For Copyright notice and legal terms of use click here.

Protected by Copyscape