Creating a Popup Date Picker

Thursday Jan 10th 2008 by Quin Street
Share:

Conrad Jalali shows how to extend the functionality of the ASP.NET Calendar control to remove some of the annoying postback delays that occur when populating a text box with a date from a popup calendar.

By Conrad Jalali

Prerequisites
  • Familiarity with C#, Visual Studio .NET, and creating ASP.NET applications
  • Basic understanding of Events in .NET
  • Basic understanding of JavaScript
  • Basic understanding of HTML

  • download source code C#
  • download source code VB .NET

Introduction

Recently, I was working on a Web Form and I wanted to give users an easy way to enter a date into a TextBox control. Back in the days of Classic ASP, I frequently used a small ASP page, opened in a popup window, to display a calendar. Users could go to the previous/next month, and when a date was clicked, the selected date would populate the input field via some basic JavaScript.

But we're in the .NET world now. I wanted to come up with an easy way to do this that would rely on the rich controls that Microsoft included with ASP.NET. After a quick trip to Google, I found a solution; add a new EventHandler to the ASP.NET Calendar control's SelectionChanged event that emits the JavaScript needed to set the value of the TextBox.

All was well in the world. I had a solution that provided the functionality that I needed, and I didn't have to use a third party control or write my own from scratch. But then I started testing the solution...

While everything worked, there was an annoying delay between the user's click selecting a date, and the date populating the TextBox. This pause, which lasted a few seconds, was driving me crazy. It didn't take long to find the culprit; the PostBack.

Even though the Calendar control was using JavaScript to set the value of the TextBox, it was doing so when the SelectionChanged event fired. This event is fired on PostBack, so there was a delay between the user's click and the data filling in the TextBox. Remember, the PostBack submits a Web Form back to itself. This causes the user to wait unnecessarily, and needlessly adds to the Web server's load.

Back to the Drawing Board

I still didn't want to create my own custom calendar control, and I wasn't satisfied with the PostBack option, so I decided to explore extending the functionality of the ASP.NET Calendar control. Specifically, the Calendar control's DayRender method piqued my interest, so I let my IntelliSense do the walking.

I found that I could clear the standard date link from each day in the calendar and create my own link. My link would include the HTML and JavaScript needed to populate the TextBox and close the popup window. Now I was in business.

Building the Solution

Let's look at the elements that will make up our solution:

  • A Web Form with a TextBox control whose value will be populated by the calendar
  • A link on the Web Form with some JavaScript to launch the date picker popup
  • A Web Form for the date picker popup
  • A Calendar control on the date picker page with an extra DayRenderEventHandler attached
  • A DayRenderEventHandler delegate to replace the standard date links with a custom JavaScript link

The Web Form is straight forward:


Figure 1

Here is the HTML for the "pick" link, which launches the date picker popup:

<a href="javascript:;" onclick="calendarPicker('Form1.txtEventDate');" title="Pick Date from Calendar">pick</a>

Note that since the tag does not have the runat="server" attribute, the onClick event will fire on the client-side (in the browser), not on the server.

As you can see, the link calls a JavaScript function called, calendarPicker. I prefer to call a function with the link, rather than writing the code in-line, as there may be more than one date field on a form. If we write the code in a function that uses a parameter to specify the reference to the field that we want to set, we can reuse the code for as many TextBox controls as we like on a single page. We can also debug JavaScript functions, but more on that, later.

I like to pass the calendarPicker function the name of the field that we want to edit in the format, "FormName.FieldName". You might have occasion to substitute your own form ID for VS .NET's standard "Form1", so I don't like to hard code this.

Here is the calendarPicker function:

<script language="javascript" type="text/javascript">

/// <summary>

/// Launches the DatePicker page in a popup window,

/// passing a JavaScript reference to the field that we want to set.

/// </summary>

/// <param name="strField">String. The JavaScript reference to the field that we want to set,

/// in the format: FormName.FieldName

/// Please note that JavaScript is case-sensitive.</param>

function calendarPicker(strField)

{

window.open('DatePicker.aspx?field=' + strField, 'calendarPopup', 'width=250,height=190,resizable=yes');

}

</script>

The calendarPicker function opens a page called DatePicker.aspx in new window. It passes the name of the field that we want the calendar to set in the QueryString. I like to use Visual Studio-style comments for my client-side scripts since it gets me in the habit of documenting my code.

Next, the DatePicker ASPX page:


Figure 2

The DatePicker page contains one standard ASP.NET Calendar control:

<%@ Page language="c#" Codebehind="DatePicker.aspx.cs" AutoEventWireup="false" Inherits="CalendarPopup.DatePicker" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >

<html>

<head>

<title>DatePicker</title>

<meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">

<meta name="CODE_LANGUAGE" content="C#">

<meta name="vs_defaultClientScript" content="JavaScript">

<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">

<style type="text/css">

BODY { PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 4px; PADDING-TOP: 0px }

BODY { FONT-SIZE: 9pt; FONT-FAMILY: Verdana, Geneva, Sans-Serif }

TABLE { FONT-SIZE: 9pt; FONT-FAMILY: Verdana, Geneva, Sans-Serif }

TR { FONT-SIZE: 9pt; FONT-FAMILY: Verdana, Geneva, Sans-Serif }

TD { FONT-SIZE: 9pt; FONT-FAMILY: Verdana, Geneva, Sans-Serif }

</style>

</head>

<body onblur="this.window.focus();" ms_positioning="FlowLayout">

<form id="Form1" method="post" runat="server">

<div align="center">

<asp:calendar id="Calendar1" runat="server" showgridlines="True" bordercolor="Black">

<todaydaystyle forecolor="White" backcolor="#FFCC66"/>

<selectorstyle backcolor="#FFCC66"/>

<nextprevstyle font-size="9pt" forecolor="#FFFFCC"/>

<dayheaderstyle height="1px" backcolor="#FFCC66"/>

<selecteddaystyle font-bold="True" backcolor="#CCCCFF/>

<titlestyle font-size="9pt" font-bold="True" forecolor="#FFFFCC" backcolor="#990000"/>

<othermonthdaystyle forecolor="#CC9966"/>

</asp:calendar>

</div>

</form>

</body>

</html>

The BODY tag contains a simple script that runs in the onBlur event (clientside) to turn the popup into a modal dialog box. Since we want the popup to be small, and its only purpose is to set a field value in the main page, there's no reason to let the user switch back to the other window.

The magic happens in a method that we attach to the Calendar's DayRender event:

/// <summary>

/// Replaces the standard post-back link for each calendar day

/// with the javascript to set the opener window's TextBox text.

/// Eliminates a needless round-trip to the server.

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void Calendar1_DayRender(object sender, System.Web.UI.WebControls.DayRenderEventArgs e)

{

// Clear the link from this day

e.Cell.Controls.Clear();

 

// Add the custom link

System.Web.UI.HtmlControls.HtmlGenericControl Link = new System.Web.UI.HtmlControls.HtmlGenericControl();

Link.TagName = "a";

Link.InnerText = e.Day.DayNumberText;

Link.Attributes.Add("href", String.Format("JavaScript:window.opener.document.{0}.value = \'{1:d}\'; window.close();", Request.QueryString["field"], e.Day.Date));

// By default, this will highlight today's date.

if(e.Day.IsSelected)

{

Link.Attributes.Add("style", this.Calendar1.SelectedDayStyle.ToString());

}

 

// Now add our custom link to the page

e.Cell.Controls.Add(Link);

}

This method will be called each time the Calendar control renders a day. The first thing that our method does is clear the contents of the table cell for the day that's being rendered. Next, we programmatically create a hyperlink (an <A> tag). We use the current day number from the Calendar as the InnerText for the link, just like the Calendar control would normally have done. We set the "HREF" property of the link by building a string that has the JavaScript needed to set the value of the TextBox control in the opener window, and then close the DatePicker window.

In the JavaScript, "opener" is a keyword that refers to the window that opened the current window via a window.open command. ASP.NET TextBox controls are renedered as <INPUT> tags in HTML. The script continues by setting the value of the particular field that we want to change, and in this case, we're specifying that the selected date be used (formatted with the ShortDatePattern) to set the value of the field.

In our Calendar1_DayRender method, we then check to see if the day that we're on represents today's date. Since we haven't specified the Calendar's SelectedDate property, it will select today's date by default. I like to give users a reference point, so I add a "style" attribute to our link and use the Calendar's SelectedDayStyle.

We wrap everything up by inserting our replacement hyperlink in the table cell for the day that we're on.

Things are going along fine, but we still haven't re-wired the Calendar control to call our custom method. To attach Calendar1_DayRender to the DayRender event for the Calendar, we can either edit DatePicker.aspx in Design mode in VisualStudio .NET or directly modify the page's InitializeComponent method in the code-behind file.

To avoid messing with the auto-generated code in the InitializeComponent method, we can add the EventHander by viewing the DatePicker page in Design mode. Click on the Calendar control and then click on the Events icon (the lightning bolt) in the Properties box. We should be able to select the method, Calendar1_DayRender, in the drop down menu next to the DayRender action.

VS .NET populates the available choices in the drop down menu by searching the code-behind file for any methods that match the ASP.NET Calendar control's DayRender event delegate signature. (The DayRenderEventHandler delegate takes two parameters; an object and a DayRenderEventArgs type.)


Figure 3

If we prefer to attach the custom delegate ourselves (or simply want to take a look at the changes made through the VS.net IDE), we can find the InitializeComponent method in the code-behind file in a region labeled "Web Form Designer generated code".

/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{

this.Calendar1.DayRender += new System.Web.UI.WebControls.DayRenderEventHandler(this.Calendar1_DayRender);

this.Load += new System.EventHandler(this.Page_Load);

}

Notice that we're adding our DayRenderEventHandler, not replacing the Calendar control's built-in EventHandler. This is indicated by the += syntax. Let's let ASP.NET do the work of creating the HTML table for the calendar, and all of the days in the month. We're just going to substitute our own content for each day's table cell.

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved