Creating a DropDownLabel Server Control
 
Published: 30 Aug 2005
Unedited - Community Contributed
Abstract
Server controls are the life force behind ASP.NET development. The server controls that come with the ASP.NET framework are fantastic, but sometimes they do not perform the exact task that is required. Luckily, Microsoft has designed the server control model so that developers can extend and implement their own custom server controls based on the ASP.NET control set. This article will explain some of the concepts in custom server control design, and also provide a walkthrough on how to create a custom DropDownLabel control.
by Matthew Mondok
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 21315/ 58

Introduction

Download Code

Recently I needed to refactor an Access project to an ASP.NET application, and I found that the Access application contained a ComboBox that displayed the selected value, not the item, in a label.  I figured that I could easily find an ASP.NET server control that provided the same functionality, but I had no luck.  My only choice was to create my own.  My initial instinct was to use a client-side callback, or AJAX, but I needed this control in literally hundreds of places.  Obviously, I had no choice but to create a server control.  In this article, I will explain not only how to create this specific control, but also how to get started creating server controls in general.

 

A [Very] Short Overview of a Server Control

Server controls, besides HTML controls, are one of the basic building blocks for creating ASP.NET applications.  Ultimately, all the standard ASP.NET server controls render out to HTML, but some contain more functionality than others.  For proof, just do a View Source of any aspx page.  In order to create server controls, one must understand exactly how they are rendered to the page.  Server controls are extensions of the tools that we as developers are given.  ASP.NET has given us a fantastic library, but we must put it all together.  A custom server control usually extends one of the standard classes found in the System.Web.UI.WebControls namespace.  Although we could extend of of the classes within the System.Web.UI.HtmlControls namespace, it is not recommended because we lose many additional properties provided by the base WebControl class.  Within the WebControls library, we can choose to derive from many different controls, and in this article I will use the DropDownList class.

The basic methods that my server control uses are Render and PreRender from the WebControl class, and RegisterStartupScript and RegisterClientScriptBlock from the Page property.  Each of these will be explained briefly, but I would suggest doing further reading on the MSDN website to get a full understanding of what is actually happening.

Understanding Render and PreRender

The DropDownLabel control overrides two methods, Render and OnPreRender.  In general, the Render method writes the required HTML to the page.  The OnPreRender method adds any necessary data to the ViewState of the page, prior to writing out the HTML or rendering the content.  Typically, any DHTML should be generated and registered in the OnPreRender method.

Understanding RegisterStartupScript and RegisterClientScriptBlock

Two other methods that we will be using are available from the Page property.  These two methods are RegisterStartupScript and RegisterClientScriptBlock.  The RegisterClientScriptBlock method emits any script directly before the closing server-side form tag.  The RegisterClientScriptBlock method emits any script directly after the opening server-side form tag.  RegisterClientScriptBlock is perfect for DHTML functions because it cannot access any form objects, but it is great to get a function ready to be accessed.  Conversely, the RegisterStartupScript runs after the form is initialized and it can be called immediately without throwing DHTML errors.  Basically, if I wanted to call some script that would run equivalent to the OnLoad event, I would use the RegisterStartupScript method.  If I wanted to add a DHTML function to the page, I would use the RegisterClientScriptBlock method. 

All in all, understanding the mechanics of how the events that fire before a page is rendered is imperative to creating server controls.  Obviously, a quality server control cannot be created if the developer does not understand what happens at the presentation layer.  That being said, I will now explain the steps to creating the control.

Creating the DropDownLabel Control

The DropDownLabel is a composite control consisting of a DropDownList and a Label.  The control extends the DropDownList class.  If the control were to extend the Label class, it would lack the correct attributes to be of any use at all.  It should also be noted that the control is written in C#.

The scope of the control must be defined first.  The scope includes the following:

Listing 1 - Control Scope

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Text;
namespace DropDownLabelControl

Everything but System.Text is standard.  System.Text is defined because it allows for use of the StringBuilder class.  As a side note, when defining very long strings, it is much more proficient to use StringBuilder rather than concatenation via the “&” (VB) or “+” (C#).  The only downside is that concatenating thousands of lines of code can be a little annoying.  This article only uses the “+” to concatenate variables.

The next step in any server control is to define how it will show up in the toolbox and the HTML.  Listing 2 shows how I defined my control.

Listing 2 – Defining the Control’s Designer Properties

DefaultProperty("Text"), 
ToolboxData("<{0}:DropDownLabel runat=server></{0}:DropDownLabel>")] 

The control will show up in the HTML designer something like the following:
   <cc1:DropDownLabel id="ddlMyControl" runat="server"></cc1:DropDownLabel>
The basic syntax should make sense.  If you wish to define a tag prefix other than the default, this is done in the AssemblyInfo.cs file of your project. 

Inheriting a Control’s Properties

Since the main goal in the DropDownLabel is to keep the functionality of a DropDownList, but show the selected value in a label, it should inherit the DropDownList’s properties.  If it were to inherit from the Label class, or from the WebControl class, then properties would have to be written for the DropDownLabel to have databinding methods.  That’s way too much work when it can just inherit all that databinding code, as shown in Listing 3.

Listing 3 – Inheritance with a DropDownList

public class DropDownLabel : DropDownList 

The Logic behind the Control

Since the control is going to display a value in a Label, it needs to render a Label next to the actual DropDownList.  By inheriting the DropDownList’s base class, there is no need to worry about preserving the control’s value during a postback because the DropDownList will, by default, add the value to the control’s ViewState.  The Label will maintain accuracy by calling a DHTML function every time the page posts back to the server.  If the control did not call a DHTML function every time the page posted back, the label would always be blank after a postback because the client-side events would not have been added to the viewstate.

The OnPreRender Event

DHTML should be created during the OnPreRender event, and this is where the control will be using the RegisterStartupScript and RegisterClientScriptBlock methods of the Page.  The DHTML function is going to receive the ID value of the server control as an argument, and then it is going to find the DropDownList and the Label using the getElementById JavaScript function.  Finally, the function is going to set the innerHTML of the Label equal to the value of the DropDownList.  Voila!  That’s all there is to the function.  Now that the function is written, all that needs to be done is to register it within the Page using its RegisterClientScriptBlock method.  Listing 4 shows the RegisterClientScriptBlock method in full.

Listing 4 – RegisterClientScriptBlock

StringBuilder sbFunction = new StringBuilder();
if(!this.Page.IsClientScriptBlockRegistered("LabelScript" + this.ClientID ))
{
      sbFunction.Append("<script language=javascript>");
      sbFunction.Append("function ChangeLabelText" 
            + this.ClientID + "(id){");
      sbFunction.Append("var lblDisplay=document.getElementById('lblDisplayText" 
            + this.ClientID  + "');");                      
      sbFunction.Append("var ddlDropDown=document.getElementById(id);");
      sbFunction.Append ("lblDisplay.innerText=ddlDropDown.value;}");                     
      sbFunction.Append("</script>");
      this.Page.RegisterClientScriptBlock("LabelScript" 
            + this.ClientID ,sbFunction.ToString());
}

The second half of the OnPreRender event is for the DropDownList to call the DHTML function when the value changes.  The client-side event that will fire when the value changes is the onChange event.  Really, the code in Listing 5 is doing two things.  First, it is registering the startup script so that the DHTML function is called when the page posts back and when the page is loaded.  Second, it tells the DropDownList that in the onChange event, it should call the DHTML function that was created earlier. 

Listing 5 – RegisterClientScriptBlock

StringBuilder sbStartupScript = new StringBuilder();
if (!this.Page.IsStartupScriptRegistered("LabelStartupScript" 
      + this.ClientID))
{
      sbStartupScript.Append("<script language=javascript>");
      sbStartupScript.Append("ChangeLabelText" 
            + this.ClientID + "('" + this.ClientID + "');");
      sbStartupScript.Append("</script>");
      Page.RegisterStartupScript("LabelStartupScript" 
            + this.ClientID,sbStartupScript.ToString());
}
this.Attributes["onchange"]= "ChangeLabelText" 
      + this.ClientID + "('" + this.ClientID + "')";
base.OnPreRender (e);
}

At the conclusion of the OnPreRender method, the base class method must be called after the custom rendering is finished, in order to actually complete the method.  Otherwise, the DHTML may not know that the control was ever rendered out.

The Render Event

Within the Render event, we want to write out any HTML that we need.  Listing 6 shows the code that sits in the Render event.

Listing 6 – Rendering the DropDownLabel

protected override void Render(System.Web.UI.HtmlTextWriter writer) 
{           
      base.Render(writer);          
      StringBuilder sbHTML = new StringBuilder();
      sbHTML.Append("<DIV style='DISPLAY: inline; FONT-WEIGHT: normal; ");
      sbHTML.Append("WIDTH: 212px; COLOR: black; FONT-FAMILY: Verdana; ");
      sbHTML.Append("HEIGHT: 19px; BACKGROUND-COLOR: silver' ");
      sbHTML.Append("id='lblDisplayText" + this.ClientID + "' runat='server'>" 
            + "</DIV>");
writer.Write(sbHTML.ToString());          
}

The code above creates a simple HTML label.  The most important note here is that the DropDownLabel’s client name is concatenated to the label id.  This makes sure that the control is uniquely identified on every page.  Things could disastrous if two controls had the same name!  Also note that the DropDownList is rendered prior to writing out the HTML.  If base.Render(writer) were to execute after the HTML, the DropDownList would be displayed after the label.

Wrapping Things Up

One final thing that I mentioned, but did not explain thoroughly, was why I concatenated the ClientID of the control to many different areas. Even though concatenating the ClientID is not necessary in most instances, it is still safe practice. If DHTML function names are generic enough, the possibility exists for two completely different controls to register the same function. The chances are slim to none, but if it would happen, then there would be some major debugging issues.

Also, although this control doesn’t have any extended properties that can be set, it does have the possibility to add quite a few. Different properties like setting the color of the label can easily be added. Another nice addition would be the ability to set a blank value at the very top, especially since the purpose of the control is to mimic a Microsoft Access ComboBox. Validation would also be a nice addition that could be done on the client side of things.

Hopefully, this article was very helpful in understanding the basics behind a server control. For further reading, MSDN provides great documentation. There are also a few books out there that discuss the creation of server controls, although all the needed information is available from MSDN.



User Comments

No comments posted yet.






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-10-09 2:19:06 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search