ASP.NET calendar enhanced for JS and mobile devices

0
71

Introduction

This is a simple (yet elegant, lightweight, and responsive) event calendar using the ASP.NET calendar control, an UpdatePanel (to avoid full postbacks), and a bit of jQuery that displays events for the selected day.

Background

I needed to present my users with a straightforward event calendar, but I didn’t want or need a “full” calendar with multi-day events.  My users purchase their participation in various activities using other screens, so the calendar was needed as a way to consolidate all their purchased activities in a visual manner, for easy review on any device..

There are many event calendar plug-ins available (search and you will find dozens), but I needed something very lightweight, and preferred not to install and integrate a third-party package.  So, I adapted the built-in ASP.NET calendar.

Using the code

The default ASP.NET calendar control (unstyled) looks like this:

This is not very polished or user-friendly, so the first step is to build out the control with class names so that a style sheet can be used.

I added a “calendarWrapper” div around the calendar, so I could give it a nice-looking container:

<div class="calendarWrapper">
   <asp:Calendar ID="Calendar1" runat="server" CssClass="myCalendar" DayNameFormat="Short" Font-Names="Tahoma"
      OnDayRender="Calendar1_DayRender" OnVisibleMonthChanged="Calendar1_VisibleMonthChanged" 
      cellspacing="0" cellpadding="0" title="Calendar" 
      style="border-width: 1px; border-style: solid; font-family: Tahoma; border-collapse: collapse;">
          <OtherMonthDayStyle CssClass="calDay otherMonthDay" />
          <DayStyle CssClass="calDay" />
          <DayHeaderStyle CssClass="calDayHeader" ForeColor="#2d3338" />
          <SelectedDayStyle Font-Bold="True" CssClass="calDaySelected" />
          <TodayDayStyle CssClass="calToday" />
          <SelectorStyle CssClass="calSelector" />
          <NextPrevStyle CssClass="calNextPrev" />
          <TitleStyle CssClass="calTitle" />
  </asp:Calendar>
</div>

With my style-sheet, the result is:

Note that CSS3 is being used heavily here — you can see two drop-shadows in the image above.  The outer light-blue border is the wrapper div (with drop-shadow added), which gives the calendar a nice finishing touch.

The CSS file is included in the ZIP file, and you can experiment with it to get the results you want — this article will focus more on the JS and the code-behind, and how to minimize database activity.

Since my users may have dozens of events in a given month, I anticipate that they may click around on the calendar to view each day.  I wanted to avoid the DB hit on each click, so I set out to retrieve all events ahead of time so I could use JS (jQuery) to instantly display a day’s contents upon click/tap.  No postback, no DB query, no delay.

Doing the advance query turned out to be tricky, because there is no way to ask the calendar control for its date-range, to know which days to query for.  We can’t even ask it for its current month on initial load, though this is easy enough to figure out (DateTime.Now.Month).

However, the control always displays 6 weeks, or 42 days — and it starts (by default) on Sunday, so I used an extension method to calculate the control’s anticipated first day:

    public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
    {
        int diff = dt.DayOfWeek - startOfWeek;
        if (diff < 0)
        {
            diff += 7;
        }

        return dt.AddDays(-1 * diff).Date;
    }

….which provides the start-date, so just add 42 to get the end date.  Then use this date-range to query the database for applicable events in that time period.

Next, we have to send the event information to the browser so that javascript code can display it (perhaps in another div, or a tooltip-style popup).  I chose to have 42 hidden fields, corresponding to the calendar dates, to hold the strings.  Then I just needed each click to invoke a JS call to display the events for the day being clicked.  

To do this we have to replace the __doPostBack call in the anchor tag inside each date-cell.  This requires implementing the control’s DayRender event, where we replace the anchor itself with a new one:

protected void Calendar1_DayRender(object sender, DayRenderEventArgs e)
{
   DateTime thisDate = e.Day.Date.Date;
 .
 .
 e.Cell.Text = string.Format("<a href='#' onclick='Showday(this,{0});return false;' title='{1}'>{2}</a>",
  _dayIndex.ToString(), thisDate.ToString("dddd, MMMM d, yyyy"), thisDate.Day.ToString());
 .
 .
}

The anchor title is set with a long date-string for two reasons:  (1) when the user hovers on the date, a tooltip will show the title, and (2) the “Showday” JS function can get the title and populate a label elsewhere on the page, for visual benefit to the user.  And, of course, the “{2}” parameter is the day itself, which the user sees in the calendar, and which is styled in the CSS.

Next, I wanted to highlight all days containing events, so the user would know where to click/tap.  In the DayRender method, I just add “ActivityDay” to the class-name for the cell, then use CSS to give it a background-image:

Any highlight method can be used, such as a background color, just modify the CSS.

Mobile devices

The CSS file specifies smaller sizes for the calendar and its cells for max-width 600 displays.  (Don’t forget the “viewport” meta tag in the markup file.)  You can tinker with the values if you want to make it even smaller, but the calendar will quickly become difficult to navigate if the cells become smaller than a finger tip.

Also, you’ll notice some “:hover” entries in the CSS file (now commented out) to give a light-gray background to cells when the mouse-cursor passes over them.  However, iOS devices do not like the hover style, and may even refuse to acknowledge a finger-tap on an anchor that has a hover style.  This is resolved by adding onmouseover and onmouseout events using the code-behind, and removing the hover CSS.  We get the same effect without offending the iPhone or iPad.

Points of Interest

Obviously, I jumped through some hoops in the code-behind to avoid as many DB calls as possible.  Another approach is to make an AJAX call on each day-click by the user.  This could avoid the initial DB query, but then you’d incur the DB hit on every click.  Trade-offs.  The AJAX-based approach could be better in cases where you need to retrieve larger amounts of data upon click

The downloadable ZIP file contains a fully-functioning website project, done in VS 2010.  I originated it in VS 2015, but wanted it to be usable in a previous version.

Most of the CSS was pieced together from other articles.  If you see it (or similar) elsewhere, most likely THAT is the original source.

History

Posted, June 13 2017.

LEAVE A REPLY