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);
}
}