BetterCalendar
View a screen shot of this control.The Demo Project
Several features of the BetterCalendar
control can be seen in
the demo project. The project contains a single web form which has both a
regular Calendar
control and a BetterCalendar
control.
Both calendars are styled using only CSS classes defined in an external
style sheet (the Styles.css file). Different styles are used for
weekend days, days outside the current month, selected days, etc. You can
immediately see how the Calendar
control fails to deal with style
classes properly.
In the code-behind, the BetterCalendar
control is initialized
to set MinVisibleDate
and MaxVisibleDate
to 90 days
before and after the current date, respectively.
private void Page_Load(object sender, System.EventArgs e) { // On the initial load, set the min and max view date to 90 days // before and after today's date, respectively. if (!this.IsPostBack) { this.BetterCalendar1.MinVisibleDate = DateTime.Today.AddDays(-90); this.BetterCalendar1.MaxVisibleDate = DateTime.Today.AddDays(+90); } }
If you page through the months on the BetterCalendar
control,
you'll note that the next or previous month navigation link will disappear when
you reach the corresponding date limit.
For both calendars, a DayRender
event handler is set up. Within it, we compare the day's date to this same date range. Days outside the range have their IsSelectable
property set to false
.
private void Calendar_DayRender(object sender, System.Web.UI.WebControls.DayRenderEventArgs e) { // Make days outside the view range nonselectable. if ((this.BetterCalendar1.MinVisibleDate != DateTime.MinValue && e.Day.Date < this.BetterCalendar1.MinVisibleDate) || (this.BetterCalendar1.MaxVisibleDate != DateTime.MinValue && e.Day.Date > this.BetterCalendar1.MaxVisibleDate)) e.Day.IsSelectable = false; }
By navigating to the month at either end of the date range, can see
that both calendars contain the same non-selectable days. Now, try
clicking on the month selector (the ">>" link next to the days
header). The Calendar
control selects every day in the month, even the ones that we explicitly marked as non-selectable in the DayRender
event handler.
If you select the same month on the BetterCalendar
control however, you'll see that the dates marked as non-selectable are
excluded. The same applies to weeks that contain non-selectable dates.
If you'd prefer to have BetterCalender
behave like Calendar
on week and month selections, you can just set the control's SelectAllInRange
property to true
.
Points of Interest
Control Rendering
Most of the code for BetterCalendar
is dedicated to the
overridden Render
method. The Calendar
control
produces a fair amount of HTML and there are several display options like
NextPrevFormat
, NextMonthText
,
PrevMonthText
, ShowTitle
, TitleFormat
,
ShowDaysHeader
, FirstDayOfWeek
, etc., to account for.
So, some of the work is broken out into separate functions for the sake of
readability.
For the most part, the control is built using existing controls from
System.Web.UI.WebControls
rather than by writing raw HTML. It's
much easier to use the properties and methods inherent to those controls for
assigning attributes and applying styles than it is to manually write them as
HTML.
Value Checking (or not)
No value checking is done when the MinVisibleDate
,
MaxVisibleDate
or VisibleDate
properties are set. It
would be fairly easy to add code to throw a
System.ArgumentOutOfRangeException
if any one of those properties
were assigned a value that makes VisibleDate
less than
MinVisibleDate
or greater than MaxVisibleDate
.
However, even if VisibleDate
is out of the implied range, it
causes no problems within the control. So rather than throw exceptions, it's
left to the application programmer to ensure that those properties are
consistent. This allows you some flexibility as you don't need to worry about
setting these properties in any particular order.
Post Back Events
The Calendar
control uses post back links to handle the next
and previous month navigation and date selection. When viewing a calendar in a
browser, you can hover over a date link and see something like:
javascript:__doPostBack('Calendar1','1646')
for the URI. 'Calendar1
' is the control ID and, obviously,
'1646' somehow refers to a date. After a little guesswork and experimentation,
it turns out that these values represent the number of days between a given
date and January 1, 2000.
In order to use the same scheme in BetterCalendar
, a
DateTime
constant and two methods are defined:
private static readonly DateTime DayCountBaseDate = new DateTime(2000, 1, 1); // // Returns the number of days between the given DateTime value and the // base date. // private int DayCountFromDate(DateTime date) { return ((TimeSpan) (date - BetterCalendar.DayCountBaseDate)).Days; } // // Returns a DateTime value equal to the base date plus the given number // of days. // private DateTime DateFromDayCount(int dayCount) { return BetterCalendar.DayCountBaseDate.AddDays(dayCount); }
For the next and previous month post back links, the argument is prefixed with the letter 'V':
javascript:__doPostBack('Calendar1','V1613')
which means "change the visible month to June 1, 2004".
For the post back links to select a week or the entire month, the argument begins with the letter 'R' followed by the day count number, and finally, two digits representing the number of total number of days to be selected:
javascript:__doPostBack('Calendar1','R164607')
which means "select seven consecutive days starting with July 4, 2004".
The RaisePostBackEvent
event implemented in
BetterCalendar
handles parsing this argument and performing the
appropriate action (changing the visible month or selecting a date or range of
dates).