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
.
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 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
private TextBox _TxtDate = new TextBox();
private Image _ImgDate = new Image();
private string _ImageUrl;
private bool _AutoPostBack;
private string _Value;
private string _ImageCssClass;
private string _TextBoxCssClass;
[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);
}
}
protected virtual void OnCommand(CommandEventArgs commandEventArgs)
{
CommandEventHandler eventHandler = (CommandEventHandler)Events[EventCommandObj];
if (eventHandler != null)
{
eventHandler(this, commandEventArgs);
}
base.RaiseBubbleEvent(this, commandEventArgs);
}
private void OnTextChanged(object sender, EventArgs e)
{
if (this.AutoPostBack)
{
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)
{
base.OnInit(e);
_TxtDate.ID = this.ID + "t";
_TxtDate.AutoPostBack = this.AutoPostBack;
_TxtDate.Text = this.Value;
_TxtDate.TextChanged += new System.EventHandler(this.OnTextChanged);
_TxtDate.Attributes.Add("readonly", "readonly");
_TxtDate.Attributes.Add("class", this.TextBoxCssClass);
_ImgDate.AlternateText = "imageURL";
if (!string.IsNullOrEmpty(_ImageUrl))
_ImgDate.ImageUrl = _ImageUrl;
_ImgDate.ID = this.ID + "i";
_ImgDate.Attributes.Add("class", this.ImageCssClass);
}
Now we will add and render controls:
protected override void CreateChildControls()
{
this.Controls.Add(_TxtDate);
this.Controls.Add(_ImgDate);
base.CreateChildControls();
}
public override void RenderControl(HtmlTextWriter writer)
{
_TxtDate.RenderControl(writer);
_ImgDate.RenderControl(writer);
RenderContents(writer);
}
Replace or add your RenderContents function with:
protected override void RenderContents(HtmlTextWriter output)
{
StringBuilder calnder = new StringBuilder();
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.
View this article on my blog.
Your feedback is welcome.
CodeProject