Sample Image - maximum width is 600 pixels

Introduction

I recently worked for Roland Garros Tennis Company, and I had a specific need for a web site application. Roland Garros tournament begins at the end of the May month and finishes in the middle of June. My client wanted to select his dates by looking at May and June months at the same time. For some users category, he wanted to allow them to select a maximum number of dates (for example: 3 days during the entire period of the tournament for the A users category), and prevent them from booking some special dates (so, the possibility to put forbidden dates on the calendar). And a last important point which is to select these dates during a date interval with a begin date and a end date (calendar view must be initialized with the first date month). It was impossible to find a 2 month calendar widget with all these options that looked good. I found one with almost all options: the EXCELLENT YAHOO calendar (http://developer.yahoo.com/yui/calendar/). I downloaded the JavaScript open source framework with the calendar component at http://developer.yahoo.com/yui/2/. I saw that the essentials functionalities were present, but 2 functionalities were missing: the possibility to set forbidden date and to set a maximum date number. So I deeply analyzed the big "calendar.js" file, and because the design of the code is excellent, very clean and very well commented, I managed to implement these 2 functionalities in this JavaScript file. ;)

In this article, I won't explain my changes in calendar.js but rather the interesting points to do the implementation of the web control.

Background

In this section, I'll explain how to use the control and what the properties are.

Mode: The Mode property is an enumeration of 3 possible values. Flat value shows the calendar like any classical widget. CollapsibleCalendarWithDateSummury value shows the calendar when you click on the image with the ImageCalendarUrl property. So, if you use CollapsibleCalendarWithDateSummury or CollapsibleCalendarWithNoDateSummury, you must provide the ImageCalendarUrl property. CollapsibleCalendarWithDateSummury generates a mini summary of your selected dates, it's useful when you close the calendar. CollapsibleCalendarWithNoDateSummury value is exactly the same like CollapsibleCalendarWithDateSummury without the summary.

MinDateSelectable and MaxDateSelectable are dateTime properties and must be set together. It allows you to restrict your date selection interval on the calendar. For example, if you wish that your calendar allows to select dates between 19/09/2012 and 09/12/2012 (you must set MinDateSelectable = 19/09/2012 and MaxDateSelectable = 09/12/2012). Set this variable server side.

InitialSelectedDates property is a List, it allows you to select some datetime at the load of the page. Set this variable server side.

ForbiddenDates property is a List, it allows you to set a list of forbidden dates: these dates won't be selectable. You can customize the UI style of these forbidden dates in calendar.css: /*Forbidden Style*/ .yui-skin-sam .yui-calendar td.calcell.forbidden{ ##customize_here## }. Set this variable server side.

NumberOfSelectableDates is an integer property that allows you to control the number of selected dates. For example: If you wish to enable one user to select a maximum of 4 dates in calendar, set this variable to 4. Set this variable server side or client side.

HeaderTitle is a string property. It allows you set the title of the header of the calendar. Set this variable server side or client side.

ImageCalendarUrl is a string property. It's the URL of the Image in mode CollapsibleCalendarWithNoDateSummury or CollapsibleCalendarWithNoDateSummury. I advice you to set this variable client side.

SelectedValues property is a List. It allows you to get the dates selected by the user when a postback occurs from a button.

If you click on one of 2 months written on the calendar, you can select the month that you wish to see:

Sample Image - maximum width is 600 pixels

Using the Code

I made the choice to implement the widget calendar inheriting CompositeControl class. Why? Because, when you want to generate hierarchical HTML tag, the children controls ids are automatically named like this: 'ControlId_childrenControlId_subchildrenControlId_subsubchildrenControlId', etc. It's an interesting property, and I used it above all in JavaScript(replacement of _ID_). When you test the program on Defaut.aspx page, watch the HTML code source generated client side. CreateChildControls is an override method who allows me to generate the HTML. OnPreRender is an override method who allows me to append to header JavaScript files references and CSS files references id need and to generate JavaScript to execute to build and display calendar. Each JavaScript file, CSS file and picture file must be and are registered in AssemblyInfo.cs and each file is an "Embedded Resource". In the Loading directory, that must be loaded dynamically, how can I read dynamically a file which is inside the calendar assembly? I used the Assembly.GetExecutingAssembly().GetManifestResourceStream() method to read the content of the files.

//Build CalendarLoader.js Constructor
System.IO.Stream streamScriptLoader = Assembly.GetExecutingAssembly().
	GetManifestResourceStream("YuiExtAspNetCalendar.Loading.calendarLoader.js");
System.IO.StreamReader readerScriptLoader = new System.IO.StreamReader(streamScriptLoader);
string scriptLoader = readerScriptLoader.ReadToEnd();
readerScriptLoader.Close();

Calendar has several buttons (right navigation, left navigation, close button, etc.). All these buttons are drawn in one file (strange design), this file is sprite.png. It doesn't suffice to put the sprint.png in the directory, ASP.NET is not able to find it during execution. To find it in the assembly, ASP.NET needs to get the AssemblyUrl and you get it with Page.ClientScript.GetWebResourceUrl() method.

string urlImageSprite = Page.ClientScript.GetWebResourceUrl(this.GetType(),
	"YuiExtAspNetCalendar.resources.sprite.png");

Consequence: Because calendar.css needs this sprite.png, we extracted all CSS lines that contain background:url('sprite.png') server side, we replace with the good URL (AssemblyUrl):

//Add style for registering sprite.png
//We extract all CSS lines from calendar.css whose contain url(sprite.png)
string urlImageSprite = Page.ClientScript.GetWebResourceUrl
			(this.GetType(), "YuiExtAspNetCalendar.resources.sprite.png");

StringBuilder stylesExtracted = new StringBuilder();

stylesExtracted.Append(" \r\n .yui-skin-sam .yui-calcontainer .title
{background:url(\"[SPRITE_IMG_URL]\") repeat-x 0 0;border-bottom:1px solid #ccc;
font:100% sans-serif;color:#000;font-weight:bold;height:auto;padding:.4em;
margin:0 -10px 10px -10px;top:0;left:0;text-align:left;} \r\n")
	.Append(" .yui-skin-sam .yui-calcontainer .calclose{background:url
	(\"[SPRITE_IMG_URL]\") no-repeat 0 -300px;width:25px;height:15px;
	top:.4em;right:.4em;cursor:pointer;} \r\n")
	.Append(" .yui-skin-sam .yui-calendar .calnavleft{background:url
	(\"[SPRITE_IMG_URL]\") no-repeat 0 -450px;width:25px;height:15px;
	top:0;bottom:0;left:-10px;margin-left:.4em;cursor:pointer;} \r\n")
	.Append(" .yui-skin-sam .yui-calendar .calnavright{background:url
	(\"[SPRITE_IMG_URL]\") no-repeat 0 -500px;width:25px;height:15px;
	top:0;bottom:0;right:-10px;margin-right:.4em;cursor:pointer;} \r\n")
	.Append(" .yui-skin-sam .yui-calendar a.calnav:hover{background:url
	(\"[SPRITE_IMG_URL]\") repeat-x 0 0;border-color:#A0A0A0;cursor:pointer;} \r\n")
	.Append(" .yui-skin-sam .yui-calcontainer .yui-cal-nav
	.yui-cal-nav-btn{border:1px solid #808080;background:url
	(\"[SPRITE_IMG_URL]\") repeat-x 0 0;background-color:#ccc;margin:auto .15em;} \r\n")
	.Append(" .yui-skin-sam .yui-calcontainer .yui-cal-nav
	.yui-cal-nav-btn.yui-default{border:1px solid #304369;background-color:
	#426fd9;background:url(\"[SPRITE_IMG_URL]\") repeat-x 0 -1400px;} \r\n");

	stylesExtracted = stylesExtracted.Replace("[SPRITE_IMG_URL]", urlImageSprite);

The last point is if we put several calendars in the same page, it's useless to include several times js files and CSS files libraries. So I used Page.Items collection which is the persistence is the life cycle of an HttpRequest.

Consequence: If you put several calendar in the same page, I did the treatment to load the script once.

Points of Interest

I improved my knowledge about JavaScript encapsulation in ASP.NET component. I wish to work in a company whose business is the design of toolbox ASP.NET components.

I tested this component several times, in lots of situation. Its behaviour looks correct. Don't hesitate if you have any questions.

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"