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

Friday, 26 June 2009

download code here

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 to here http://calendarview.org/ . What I have done here is used this JavaScript-based calendar inside a web composite control combined with a image in order to make it work like a true date picker control for asp.net.

Problem

it’s not very simple to use 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 with 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 how to make a perfect asp.net date picker control.

Things you need?

Prototype Library

http://calendarview.org/ (JavaScript and CSS files only.)

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 control with a default prefix showing as below

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 true or false. Value: To access the value of date picker e.g datepicker. ImageCssClass: To style the image. TextBoxCssClass: To Style the textbox .
//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.

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

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 dont 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
/// <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
/// <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());
    }
Above code is JavaScript code to make the calendar control to 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 chose 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 and on your page and start using it :)

<SQ:DatePicker ID="DatePicker1" runat="server"  ImageUrl="Javascript/CalendarIcon.gif" />
Adding JavaScript
<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: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
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 weeks time how to work with Scrum Management and DSDM (Dynamic Systems Development Method) and how to implement them in real time scenarios with development.

Your feedback is welcome.

Don't forget to see its updated version on my blog Asp.net date picker control part 2.

Comments

No response to “How to: Create a Date Picker Composite Control in ASP.NET (C#)”
Post a Comment | Post Comments (Atom)

Post a Comment