Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

DateTimePicker Web Control

0.00/5 (No votes)
23 Apr 2004 4  
DateTimePicker control making use of the ASP.NET Calendar control.

Sample Image - together.gif

A Sample of Date Selection with DateTimePicker

Introduction

DateTimePicker Web control is similar to the windows DateTimePicker control. The control exposes two properties: the SelectedDate and the FormatType. SelectedDate represents the date user selected and FormatType represents the format in which the date string should be displayed. The demo provided shows the usage of the control.

Background

Before getting on with the DateTimePicker, I thought I would share with you some matters I found important while developing ASP.NET composite server controls:

Need for CreateChildControls:

This composite controls derives from System.Web.UI.WebControls.WebControl as do most other composite controls. When developing composite controls, it is seldom that you don't need to override the function CreateChildControls:

cal.VisibleMonthChanged +=new MonthChangedEventHandler(month_changed);
cal.SelectionChanged +=new System.EventHandler(date_changed);

Controls.Add(dArea);
Controls.Add(cal);
base.CreateChildControls();

In general, individual controls used by a composite control should be added to its Controls collection. This has the effect of placing these constituent controls under the custom control in the control tree that ASP.NET framework generates for the page. A control tree is nothing but the equivalent of a DOM structure which the ASP.NET creates for a page. The image below illustrates a control tree for the page that housed the DateTimePicker control.

Control Tree Structure

You can generate control tree information by enabling tracing on a page by adding trace="true" to the Page directive. The above figure represents the hierarchy of various elements present on the page. Adding the TextBox and the Calendar controls to the Controls collection makes them the children of the composite control in the control tree hierarchy. The addition of constituent controls to the control tree makes sure that the events of the individual controls are suitably handled. We don't have to take extra measures for their event handling. All we need to do is add event handlers for the events we need to capture. CreateChildControls is also the best place to add event handlers for the events you may want to receive from child controls, as shown in the code. But there is something more to all these. The question of when CreateChildControls gets executed is important in successful event handling. This is where the INamingContainer comes in.

Need for INamingContainer:

Almost all composite controls implement this interface. Implementing is simple as shown:

public class DateTimePicker : System.Web.UI.WebControls.WebControl.INamingContainer

INamingContainer is a marker (empty) interface that does not have any methods. When this interface is implemented by a control, the ASP.NET page framework creates a new naming scope under that control. This ensures that child controls have unique IDs in the hierarchical tree of controls. This is evident in the control tree fig shown. The name for the control instance is dtpicker and the name of the TextBox is dtpicker_ctl0 and the name of the Calendar control is dtpicker_ctl0. Since there can be no other control on the page with name dtpicker, we can be sure that the name of the TextBox and Calendar are unique. This feature is important when there are multiple instances of a control on a page. One thing to note is that this hierarchical naming scheme is important in event routing. If we give our own IDs to the child controls, then we won't be able to handle their events.

e.g.  cal.ID="mycalendar";

INamingContainer also controls as to when the CreateChildControls method for a composite control is called. Consider the ASP.NET page execution cycle of a composite control without the INamingContainer interface implementation:

LoadPostData->OnLoad->RaisePostDataChanged->
Handle events->OnPrerender->CreateChildControls->
SaveViewState->Render->Dispose

Now, compare the above with the following when the INamingContainer is implemented:

OnInit-> LoadViewState->LoadPostData->RaisePostDataChanged->
CreateChildControls->OnLoad->Handle events->OnPrerender->
SaveViewState->Render->Dispose

The ASP.NET page execution cycle represents the various steps between the request arriving for an �aspx� page on the IIS and the rendering of the page to the browser. This cycle happens every time a page is requested, i.e., this happens the first time the page is loaded and for all subsequent postbacks. From the above two sequences, we see that INamingContainer is important if we need to handle events of constituent controls. In the first sequence during the event handling phase, the child controls were not part of control tree, so the event was just ignored. (Only controls present in the control tree during event handling are candidates for event handling.) Thus we see that the naming scheme and control tree structure is central to event handling and implementing INamingContainer guarantees that all is well.

Need for ViewState

As I discussed earlier, the ASP.NET page execution cycle is executed every time a request for a page arrives at the server. Since the control instance is created from scratch every time, the values of properties when the page was rendered the last time should be saved somewhere. This is precisely what is being achieved by the ViewState property. The ViewState is a dictionary object which stores name value pairs. When a page is rendered, the Viewstate of each of the controls on the page is collectively rendered in the form of <input type=hidden>. You can see this input control in the 'View Source' of the browser. When a page is postback, the data in the input field is used to recreate the ViewState. This is evident in the page execution cycle shown above. During the OnInit phase, the control instances are created (construction), and then during the LoadViewState phase, each control is given its previous state which was saved in the page in the form of hidden control. Thus ViewState helps us to create an effect of persistent storage. All we need to do is implement properties as returning value from ViewState and writing into ViewState as here:

public string SelectedDate
{
get
{
return (string)ViewState["selecteddate"];
}
set
{
ViewState["selecteddate"]=value;
}
}
public string FormatType
{
get
{
return (string)ViewState["format"];
}
set
{
string val=(string)value;
    
ViewState["format"]=val.ToUpper();
}

The SelectedDate is to be entered in the format �mm/dd/yy�. The FormatType can be �long� or �short�. If SelectedDate is not specified or is given in some wrong format, then today's date is taken as the default.

Working of the DateTimePicker

The DateTimePicker control is a composite control which includes two server side components: the TextBox and the Calendar control. It also includes a client side button control. The textbox and button are housed in one table and the calendar in another. The need for another table arises from the fact that we need to change its visibility on button click and on focus changes. In order to achieve this via JavaScript, we need to refer it by an ID. We can�t assign an ID to Calendar control because then event handling is not possible. So we put it within a table and change the visibility of the outer table instead. All the controls are rendered at once, but the visibility of the Calendar (and in fact, the outer table) varies according to the following:

  1. When the page containing DateTimePicker is loaded the first time, Calendar is invisible.
  2. When the page is loaded after a postback which was initiated by a date change event, the Calendar is invisible.
  3. When the page is loaded after a postback which was initiated by a month change event, the Calendar is visible.

When the calendar is visible at page load, we need to set its z-Index property to some high value for achieving a 3D effect (the calendar appears over the controls which it overlaps). But for this, we need a control which has an OnLoad event. Since we haven�t used one such control till now, I simply generate a dummy IFrame and do the housekeeping works in the OnLoad event of IFRAME. There�s also an input hidden control that is generated which specifies whether the Calendar is visible or otherwise. This control also helps in back button handling. We won�t look into the details of the JavaScript function. You can find these functions in the Render method. I have commented it to my best and hopefully it will be enough to get a feel of how the controls work. You may have now realized as to why I overloaded the Render method within the control, since I had to output some extra controls and their supporting JavaScript functions. If you have only your child server controls to render in a composite control, there is no need for the render method. One thing to remember in this case is that there can be several instances of one control on a page. So HTML component names created in the Render method should be unique:

This is achieved by generating the names by appending this.UniqueID to function and component names. This is how it was done here:

string uniqueID=this.UniqueID;
string spanID="main"+uniqueID;
string btnID=uniqueID+"btn";
string tdID=uniqueID+"td;
string hidID=uniqueID+"_a1;

this.UniqueID represents the name your control as given in the page it is used. Since no two controls can have the same name component, names are bound to be unique.

Implementing Design Time Support

Implementing design-time support involves writing a new class which overrides the class System.ComponentModel.Design.ControlDesigner.

The members that need to be overridden include GetDesignTimeHtml and AllowResize. Some portion of the class implementation is shown below:

public class MyDesigner:ControlDesigner
{
  string width;
  public override bool AllowResize
  {
    get
    {
       return true;
    }
  }
  public  override string GetDesignTimeHtml()
  {
    string designTimeHtml;
    DateTimePicker controlDesigned=(DateTimePicker)this.Component;
    string dateDesignTime=controlDesigned.SelectedDate;
    string format=controlDesigned.FormatType;
    //some more

    ...
  }

This is the function which is called by the designer to get the �HTML� which represents your control in the design view. This function is called when a control is placed on a page in the design view and every time the control is moved or resized. The designer generates the visual form for the HTML returned from the function. The component property returns a reference to the control instance that is being designed. There may be some properties whose values we may need to render at design time. These values can be obtained by the instance pointed by the component property.

AllowResize method simply returns true if your control supports resizing. In order to inform the designer that MyDesigner is the designer class for this control, simply add the following attribute to the DateTimePicker class:

[Designer("DTPicker.MyDesigner")]
public class DateTimePicker :

Implementing IntelliSense Support

IntelliSense support is of use when one is creating an aspx page without the design view, i.e., you are typing out the entire content of the .aspx page.

The figure below illustrates the intellisense support for this control:

Intellisense support is implemented by writing a schema file which defines the attributes your control can have and the various elements that can come within its beginning and closing tag. (Here there are no elements). I won�t go into the details of writing a schema file. One can find enough samples in the web. The only thing to remember is that the targetNamespace exposed in the schema should be the same as the namespace of the control.

The namespace in the DLL and targetNamespace of schema file are both DTPicker. The schema file for this control is provided in the download section.

The next step is to copy the schema file into the folder:

C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\schemas\xml.

This is where the schema for all your server controls reside.

About the Samples

The DateTimePickerControl code in the download is the project folder of the control. After downloading, compile the project to generate the DateTimePicker DLL. If you are using the control from the design view, simply add the DLL to the ToolBox. The properties SelectedDate and FormatType can be set from the Properties window.

If you don�t use the Design View, then add a reference to the DateTimePicker DLL in the ASP.NET project. Now, add the following Register directive.

<%@ Register TagPrefix="dtp" Namespace="DTPicker" Assembly="DTPicker" %>

When you add the first instance of the control in Design View, this directive is automatically added to the page. But the TagPrefix in this directive generated by the designer will be some value like �cc1�,�cc2�, etc.

The name of the control is DateTimePicker, so the following tag represents our control.

<dtp:DateTimePicker>

In the Design View, you may have the name <cc1:DateTimePicker> or something similar depending on the TagPrefix.

Implementing IntelliSense support involves adding the following attribute to the <HTML> tag of the page housing the control.

<HTML xmlns:dtp=�DTPicker�>

The above line assumes that the TagPrefix for the control is dtp. If TagPrefix is cc1 then the HTML tag changes to:

<HTML xmlns:cc1=�DTPicker�>

DTPicker is the targetNamespace of the schema file.

There is a sample aspx page and code-behind page provided in the download section as an example of usage of the control.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here