Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

How to: Create a Date Picker Composite Control in ASP.NET (C#)

3.90/5 (6 votes)
2 Jul 2009CPOL4 min read 52.4K   1.1K  
How to create a Date Picker Composite Control in ASP.NET (C#)

Introduction  

Composite controls are controls that combine multiple controls together to form a new reusable control. For example, a simple composite control could consist of both a Label control and a TextBox control. This example, however, puts one TextBox with an Image control together to allow a user to pick a date. The Calendar widget is a JavaScript-based calendar using prototype that can either be embedded within a page or popup when a trigger element is clicked. It is based very loosely on the Dynarch Calendar, only in the sense that it was used as a base, but has been almost entirely re-implemented. You can read more about this JavaScript-based calendar here. What I have done here is used this JavaScript-based calendar inside a web composite control combined with an image in order to make it work like a true date picker control for ASP.NET.

Problem

It's not very simple to use a JavaScript control inside a repeater, datalist or gridview because repeater uses .NET prefix with them because they don't expose commandName and commandArgument properties for calendar control.

Solution

I will extend this control with properties of CommandName and CommandArgument to use it easily inside data navigation controls.

Here we go guys! I have combined image control and the textbox control with JavaScript based control http://calendarview.org/ to make it work perfect for an ASP.NET date picker control.

How To Do it?

It’s very simple and I will guide you step by step on how to make a perfect ASP.NET date picker control.

Things You Need?

Here We Go

Using Visual Studio 2008, click on File, then New, then choose Project. Under web menu, choose ASP.NET Server Control and name the project DatePicker.

vsgui

Now we will change this control inheritance form WebControl to a CompositeControl. We will rename the toolboxdata and also will give the name to the control with a default prefix showing as below:

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.Web Controls;

[assembly: TagPrefix("DatePicker", "SQ")]
namespace DatePicker
{
  [DefaultProperty("Text")]
  [ToolboxData("<{0}:DatePicker runat="server"></{0}:DatePicker>")]
  public class DatePicker : CompositeControl
  {

Note: Instead of asp tag prefix, I have used SQ (Salman Qayyum :) Now we will add ASP TextBox, Image and properties.

Properties

  • ImageUrl: URL for image.
  • AutoPostBack: To set AutoPostBack to true or false
  • Value: To access the value of date picker, e.g datepicker
  • ImageCssClass: To style the image
  • TextBoxCssClass: To style the textbox
C#
//To retrieve value, I am using textbox
      private TextBox _TxtDate = new TextBox();
      // Image to select the calendar date
      private Image _ImgDate = new Image();
      // Image URL to expose the image URL Property
      private string _ImageUrl;
      // Exposing autopostback property 
      private bool _AutoPostBack;
      // property get the value from datepicker.
      private string _Value;
      //CSS class to design the Image
      private string _ImageCssClass;
      //CSS class to design the TextBox
      private string _TextBoxCssClass;

      /**** properties***/

      [Bindable(true), Category("Appearance"), DefaultValue("")]
      public string ImageUrl
      {
          set
          {
              this._ImageUrl = value;
          }
      }

      [Bindable(true), Category("Appearance"), DefaultValue(""), Localizable(true)]
      public string Text
      {
          get
          {
              String s = (String)ViewState["Text"];
              return ((s == null) ? string.Empty : s);
          }

          set
          {
              ViewState["Text"] = value;
          }
      }

      [Bindable(true), Category("Appearance"), DefaultValue(""), Localizable(true)]
      public string Value
      {
          get
          {

              return _Value = _TxtDate.Text;
          }

          set
          {
              _Value = _TxtDate.Text = value;
          }
      }
      [Bindable(true), Category("Appearance"), DefaultValue(""), Localizable(true)]
      public bool AutoPostBack
      {
          get
          {
              return _AutoPostBack;
          }

          set
          {
              _AutoPostBack = value;
          }
      }
      [Bindable(true), Category("Appearance"), DefaultValue(""), Localizable(true)]
      public string ImageCssClass
      {
          get
          {
              return _ImageCssClass;
          }

          set
          {
              _ImageCssClass = value;
          }
      }

      [Bindable(true), Category("Appearance"), DefaultValue(""), Localizable(true)]
      public string TextBoxCssClass
      {
          get
          {
              return _TextBoxCssClass;
          }

          set
          {
              _TextBoxCssClass = value;
          }
      }

      [Bindable(true), Category("Custom"), DefaultValue(""), Localizable(true)]
      public string CommandName
      {
          get
          {
              string s = ViewState["CommandName"] as string;
              return s == null ? String.Empty : s;
          }
          set
          {
              ViewState["CommandName"] = value;
          }
      }

      [Bindable(true), Category("Custom"), DefaultValue(""), Localizable(true)]
      public string CommandArgument
      {
          get
          {
              string s = ViewState["CommandArgument"] as string;
              return s == null ? String.Empty : s;
          }
          set
          {
              ViewState["CommandArgument"] = value;
          }
      }

Above I have also added CommandName and CommandArgument property to make this date picker work inside data navigation controls.

Generating Bubble Event

Event bubbling enables events to be raised from a more convenient location in the controls hierarchy and allows event handlers to be attached to the original control as well as to the control that exposes the bubbled event. Event bubbling is used by the data-bound controls to expose command events raised by child controls (within item templates) as top-level events. While ASP.NET server controls in the .NET Framework use event bubbling for command events (events whose event data class derives from CommandEventArgs), any event defined on a server control can be bubbled.

C#
protected static readonly object EventCommandObj = new object();

      public event CommandEventHandler Command
      {
          add
          {
              Events.AddHandler(EventCommandObj, value);
          }
          remove
          {
              Events.RemoveHandler(EventCommandObj, value);
          }
      }
      //this will raise the bubble event
      protected virtual void OnCommand(CommandEventArgs commandEventArgs)
      {
          CommandEventHandler eventHandler = (CommandEventHandler)Events[EventCommandObj];
          if (eventHandler != null)
          {
              eventHandler(this, commandEventArgs);
          }
          base.RaiseBubbleEvent(this, commandEventArgs);
      }
      //this will be initialized to  OnTextChanged event on the normal textbox
      private void OnTextChanged(object sender, EventArgs e)
      {
          if (this.AutoPostBack)
          {
              //pass the event arguments to the OnCommand event to bubble up
              CommandEventArgs args = new CommandEventArgs
					(this.CommandName, this.CommandArgument);
              OnCommand(args);
          }
      }

We will raise this bubble event with texbox.OnTextChanged using OnInit function as below.

Note: Always create dynamic controls inside OnInit function because viewstate reloads after OnInit and before the Load event. So if you want to keep the viewstate on postback, then the best place to keep the code is inside OnInit:

C#
protected override void OnInit(EventArgs e)
      {
          //AddStyleSheet();
          //AddJavaScript();
          base.OnInit(e);

          // For TextBox
          // setting name for textbox. using t just to concat with this.ID for unqiueName
          _TxtDate.ID = this.ID + "t";
          // setting postback
          _TxtDate.AutoPostBack = this.AutoPostBack;
          // giving the textbox default value for date
          _TxtDate.Text = this.Value;
          //Initializing the TextChanged with our custom event to raise bubble event
          _TxtDate.TextChanged += new System.EventHandler(this.OnTextChanged);
          //Setting textbox to readonly to make sure user does not play with the textbox
          _TxtDate.Attributes.Add("readonly", "readonly");
          // adding stylesheet 
          _TxtDate.Attributes.Add("class", this.TextBoxCssClass);

          // For Image
          // setting alternative name for image
          _ImgDate.AlternateText = "imageURL";
          if (!string.IsNullOrEmpty(_ImageUrl))
              _ImgDate.ImageUrl = _ImageUrl;
        
          //setting name for image
          _ImgDate.ID = this.ID + "i";
          //setting image class for textbox
          _ImgDate.Attributes.Add("class", this.ImageCssClass);
      }

Now we will add and render controls:

C#
/// <summary>
     /// adding child controls to composite control
     /// </summary>
     protected override void CreateChildControls()
     {
         this.Controls.Add(_TxtDate);
         this.Controls.Add(_ImgDate);
         base.CreateChildControls();
     }

     public override void RenderControl(HtmlTextWriter writer)
     {
         // render textbox and image
         _TxtDate.RenderControl(writer);
         _ImgDate.RenderControl(writer);
         RenderContents(writer);
     }

Replace or add your RenderContents function with:

C#
/// <summary>
     /// Adding the javascript to render the content 
     /// </summary>
     /// <param name="output"></param>
     protected override void RenderContents(HtmlTextWriter output)
     {
         StringBuilder calnder = new StringBuilder();
         //adding javascript first
         calnder.AppendFormat(@"<script type='text/javascript'>
                                  document.observe('dom:loaded', function() {{
                                     Calendar.setup({{
                                     dateField: '{0}',
                                     triggerElement: '{1}',
                                     dateFormat: '%d/%m/%Y'
                                  }})
                                 }});
                           ", _TxtDate.ClientID, _ImgDate.ClientID);
         calnder.Append("</script>");
         output.Write(calnder.ToString());
     }

The above code is JavaScript code to make the calendar control work using prototype. For more help, kindly visit http://calendarview.org/.

We Are Almost There Now

Now to test this file, chose solution file add new website and name it DatePickerTest. Click on view from top menu and chose Toolbox if it’s not already opened. On toolbox, click right button of mouse and choose items, then click browse and navigate to datepicker project under bin directory, add DatePicker.dll and click OK. DatePicker has seen added to your design view.

Drag and drop on your page and start using it. :)

XML
<SQ:DatePicker ID="DatePicker1" runat="server"  ImageUrl="Javascript/CalendarIcon.gif" />

Adding JavaScript:

XML
<head id="Head1" runat="server">

  <script src="Javascript/prototype.js" type="text/javascript"></script>
  <script src="Javascript/calendarview.js" type="text/javascript"></script>
  <link href="Javascript/calendarview.css" rel="stylesheet" type="text/css" />
  <title></title>
</head>

To retrieve value useResponse.Write(DatePicker1.Value);

Inside Repeater

ASP.NET
<asp:Repeater ID="Repeater1" runat="server"  DataSourceID="SqlDataSource1"
     onitemcommand="Repeater1_ItemCommand">
     <ItemTemplate>
     <SQ:DatePicker ID="DatePicker2" runat="server" CommandName="Clicked" 
	AutoPostBack="true" ImageUrl="Javascript/CalendarIcon.gif"  />
      <%# Eval("ProductName")%><br />
     </ItemTemplate>
 </asp:Repeater>

Using ItemCommand event:

C#
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
  {
      DatePicker.DatePicker dtp = 
	(DatePicker.DatePicker)e.Item.FindControl("DatePicker2");
      if (e.CommandName == "Clicked")
      {
          Response.Write(dtp.Value);
      }
  }

Don’t forget to see my next posting in a weeks time about how to work with Scrum Management and DSDM (Dynamic Systems Development Method) and how to implement them in real time scenarios with development.

Don't forget to see its updated version on my blog  ASP.NET date picker control Part 2

View this article on my blog.

Your feedback is welcome.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)