Your first ASP.NET Custom Control

One of the strengths of ASP.NET is the ability to write your own reusable custom controls, deploy them, and use them by simply dragging them onto a form from the toolbox. This tutorial walks through the implementation of a ‘TimePicker’ custom control.

In particular, it pays attention to:

  • Deriving from the WebControl class, and customising the control’s output using the HtmlTextWriter class;
  • Customising the control using properties;
  • Adding the control to the Visual Studio toolbox and adding to a page;
  • Dealing with Postback data;
  • Validating the control like a typical web control

The TimePicker control is a drop down list containing times separated by a user-definable time-period, such as 15 minutes. The WebControl class provides a good foundation for custom control development in that it provides a number of methods, properties and events common to web controls in ASP.NET.

To start, create a new class library in Visual Studio. In this case, I’ll mimick the Microsoft naming standards and call it Freshclickmedia.Web.

Creating the control class library

To further mimick the Microsoft namespace hiearchy, I have created UI and WebControls folders.

Initial Project Layout

One this has been done, right click on the project and select ‘add new item’. Add a new item of type ‘Web Custom Control’ called .

Adding a custom control to the project

Once the new control has been added, there will be a lot of default, but unwanted code. Remove the class data so that you end up with something like this:

[ToolboxData("<{0}:TimePicker runat=server>")]
public class TimePicker : WebControl{protected override void RenderContents(HtmlTextWriter output)
{

}

The first line is an attribute that defines what text will be added to a webform when the control is dragged from the toolbox. More on this later.

The class contains a single overridden WebControl method, RenderContents. The HtmlTextWriter class parameter allows control developers to customise the HTML markup that is rendered by the control at runtime, and it is this method that we’ll look at first.

By default, a class inheriting from WebControl will have a default ‘outer’ HTML element of type Span. We’re looking to write a Select, so we’ll add a constructor to specify this outer HTML element type:

///
/// Constructor defining outer HTML element type
///
public TimePicker() : base( HtmlTextWriterTag.Select)
{

}

Now when the control is used, the outer tag will be of type Select, which is what we want. However, to output our times, we’ll need to go back to the RenderContents method. In this simple example, we’ll write out a list of times. Before we do this, we need to specify some properties as follows:

  • The default text, such as ‘Select time’;
  • The minute increment for the control
  • The selected value on a Page postback

Shown below is the code for those properties. Note that each one uses the ViewState to persist the information across postbacks, and have default values of “Select Time”, 15, and string.Empty respectively.

///
/// Gets or sets the default selection text.
///
public string DefaultSelectionText
{
  get { return ViewState["DefaultSelectionText"] == null ? "Select time" : (string)ViewState["DefaultSelectionText"]; }
  set { ViewState["DefaultSelectionText"] = value; }
}

///
/// Gets or sets the minute increment.
///
public int MinuteIncrement
{
  get { return ViewState["MinuteIncrement"] == null ? 15 : (int)ViewState["MinuteIncrement"]; }
  set { ViewState["MinuteIncrement"] = value; }
}

///
/// Gets or sets the selected value.
///
public string SelectedValue
{
  get { return ViewState["SelectedValue"] == null ? string.Empty : (string)ViewState["SelectedValue"]; }
  set { ViewState["SelectedValue"] = value; }
}

To keep things simple, the RenderContents method will simply loop from 00:00 to the last available time, based on the MinuteIncrement property value. The code is shown below:

protected override void RenderContents(HtmlTextWriter writer)
{
  // write out the options:
  writer.Write("<option value=""{0}>{1}</option>", string.IsNullOrEmpty(SelectedValue) ? " selected="selected"" : "", DefaultSelectionText);

  int minuteIncrement = Convert.ToInt32(MinuteIncrement);

  // 24 hours in a day, 60 minutes in an hour, 24 * 60 = 1440
  for (int minuteLoop = 0; minuteLoop < 1440; minuteLoop += minuteIncrement)
  {
    // convert the minuteLoop value to a meaningful time:
    int hours = minuteLoop / 60;
    int leftOverMinutes = minuteLoop % 60;

    string time = string.Format("{0:00}:{1:00}", hours, leftOverMinutes);

    writer.Write( "<option{0} value="{1}">{2}</option>", time == SelectedValue ? " selected="selected"" : "", time, time);
  }

  base.RenderContents(writer);
}

The key points from the code are that a series of <option> tags are written to the browser. Some logic is included to determine whether the current option is the selected value, in which case a ‘selected’ attribute is written out for that option value.

Before using the control, let’s spend a moment on adding it to the toolbox, and also customising the default toolbox image for the control. To use a custom toolbox image, add a 16×16 image of type .bmp to the same directory as the control class. After the image has been added to the project, select properties and select the ‘Build Action’ to be ‘Embedded Resource’.

Customising the toolbox icon for the control

Once the control has been built, do the following to add the control to the toolbox.

  1. [Optional] Right click on the toolbox and select ‘Add tab’. Enter a name for the tab and press enter.
  2. Right click on a tab and select ‘Choose items…’
  3. Browse for the control assembly
  4. Click OK to add the control to the toolbox.Adding the custom control to the toolbox

So, once the control has been added, using it is as simple as dragging it from the toolbox onto a web form.

<form id="form1" runat="server">
	<cc1:timepicker id="TimePicker1" runat="server"></cc1:timepicker>
	<asp:button id="Button1" runat="server" text="Submit"></asp:button>
</form>

The markup shown above shows a very simple ASPX page with a time picker control and a button that will initiate a postback.

When we view it in the browser, we get the following:

TimePicker control rendering with property customisation

This all looks great – exactly what we wanted, but there is one huge remaining issue. If we click the submit button, the selected time is lost, so the control may look right, but functionally it’s rather useless if we can’t access the selected time, and the selected time doesn’t persist visually on the page across page postbacks.

To sort out the postback issue, we must implement 3 things:

  • Derive our control from IPostBackDataHandler, and implement the 2 interface methods, LoadPostData and RaisePostDataChangedEvent.
  • Output an HTML ‘name’ attribute to the rendering output so that the form can pick up the control’s submitted data.
  • Call RegisterRequiresPostback to indicate that the control requires page postback data handling.

Implementing the IPostBackDataHandler interface results in the following class definition change:

public class TimePicker : WebControl, IPostBackDataHandler

The 2 interface methods, LoadPostData and RaisePostDataChangedEvent are coded as follows:

public virtual bool LoadPostData(string postDataKey, NameValueCollection values)
{
  SelectedValue = Convert.ToString(values[this.UniqueID]);
  return false;
}

public void RaisePostDataChangedEvent()
{
  // Part of the IPostBackDataHandler contract.  Invoked if we ever returned true from the
  // LoadPostData method (indicates that we want a change notification raised).  Since we
  // always return false, this method is just a no-op.
}

The LoadPostData retrieves information from the posted form data and assigns it to the SelectedValue property of the control.

The RaisePostDataChangedEvent method is an essential method here, since we’re inheriting from IPostBackDataHandler. However, it is only invoked if the LoadPostData method returns true, and in our case it returns false. Returning true can be used to raise event based on changed data, but that is beyond the scope of this tutorial.

We also need to output an HTML ‘name’ attribute to the control’s output, since this is what will be used to identify the control’s data when the form is posted to the server. To do this, override the base class’ AddAttributesToRender method:

protected override void AddAttributesToRender(HtmlTextWriter writer)
{
  writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);
  base.AddAttributesToRender(writer);
}

The last thing is to override the base class’ OnPreRender method, to allow us to notify the page on which the control is hosted that the control requires post back handling:

protected override void OnPreRender(EventArgs e)
{
  base.OnPreRender(e);    

  if (Page != null)
  {
    Page.RegisterRequiresPostBack(this);
  }
}

Rebuilding will now fix the posting back issue, and the control will retain it’s selected value property.

Another issue with the control that’s very easy to implement is validation. Placing a RequiredFieldAttribute on the page and associating it to the TimePicker control will result in the following error:

Validation error on the TimePicker Control

To resolve this issue, simply add a ValidationProperty attribute to the control class:

[ValidationProperty("SelectedValue")]
public class TimePicker : WebControl, IPostBackDataHandler

Adding this simple line will give us nice co-operation with the ASP.NET validator controls.

Validation fixed

So, that’s it for a simple custom control. In future, I’ll be doing something slightly more interesting, as I look at integrating JavaScript within a control.

One thought on “Your first ASP.NET Custom Control”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.