A Sample of Date Selection with DateTimePicker
DateTimePicker Web control is similar to the windows
DateTimePicker control. The control exposes two properties: the
SelectedDate and the
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.
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
cal.VisibleMonthChanged +=new MonthChangedEventHandler(month_changed);
cal.SelectionChanged +=new System.EventHandler(date_changed);
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
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
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
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.
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:
Now, compare the above with the following when the
INamingContainer is implemented:
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
public string FormatType
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
DateTimePicker control is a composite control which includes two server side components: the
TextBox and the
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:
- When the page containing
DateTimePicker is loaded the first time,
Calendar is invisible.
- When the page is loaded after a postback which was initiated by a date change event, the
alendar is invisible.
- 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
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 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:
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
The members that need to be overridden include
AllowResize. Some portion of the class implementation is shown below:
public class MyDesigner:ControlDesigner
public override bool AllowResize
public override string GetDesignTimeHtml()
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
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
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
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
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 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.
In the Design View, you may have the name
<cc1:DateTimePicker> or something similar depending on the
Implementing IntelliSense support involves adding the following attribute to the
<HTML> tag of the page housing the control.
The above line assumes that the
TagPrefix for the control is
cc1 then the
HTML tag changes to:
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.