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.