How to: Create a Date Picker Composite Control in ASP.NET (C#)
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?
- 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
.
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:
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 setAutoPostBack
totrue
orfalse
Value
: To access the value of date picker, e.gdatepicker
ImageCssClass
: To style the imageTextBoxCssClass
: To style thetextbox
//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 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:
/// <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());
}
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. :)
<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 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.