Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

SharePoint 2007 Sliding Window Calendar Web Part

4.56/5 (10 votes)
31 May 2008CPOL5 min read 1   303  
A SharePoint 2007 Web Part demonstrating CAML queries for retrieving regular and recurring SharePoint Calendar List items.

Introduction

SharePoint Calendar Lists not only offer standard “1:00-2:30 pm” type entries, but also entries that span multiple days, and all-day items, which have no set start or end time. Retrieving this data means learning something about CAML queries and SharePoint gotchas – and I hope this article saves you some time.

Overview

The “sliding window” calendar web part shows data for 1- 7 contiguous days. The number of days displayed can be configured via the tool pane. If the web part is set to show 3 days, you’ll see today, tomorrow, and the day after that. For example, if today is Wednesday, you’ll see Wednesday-Thursday-Friday; on Thursday, the window “slides” to Thursday-Friday-Saturday. The goal is to display a lot of information in a small amount of space. To that end, only the title of each item is listed and hyperlinked - and when the mouse hovers over an item, a tooltip displays the start and end times, title, location, and description.

screenshot of Sliding Window Calendar Web Part

Retrieving SharePoint list data

C#
using (SPWeb web = SPContext.Current.Site.OpenWeb(listCalendarUrl))
{
  SPList list = null;
  try { list = web.Lists[listCalendar]; }
  catch { }
  if (null == list)
     return;

  DateTime[] weeks;
  if (((int)StartDay + NumberOfDays) > 7)
  // the time span goes over the weekend
    weeks = new DateTime[] { StartDate, StartDate.AddDays(7) };
  else
    weeks = new DateTime[] { StartDate };

If the sliding window spans the weekend, then we’ll need to look at two weeks’ worth of items, so we’ve stored our target days in the “weeks” array, and we’ll create as many queries as we need.

C#
foreach (DateTime day in weeks)
{
  // include recurring events by using a DateRangesOverlap query
  SPQuery queryRecurring = new SPQuery();

  queryRecurring.ExpandRecurrence = true;
  queryRecurring.CalendarDate = day;

  queryRecurring.Query = "<Where><DateRangesOverlap>"
     + "<FieldRef Name=\"EventDate\" /><FieldRef Name=\"EndDate\" />" +
     "<FieldRef Name=\"RecurrenceID\" />"
     + "<Value Type=\"DateTime\"><Week />" +
     "</Value></DateRangesOverlap></Where>";
     SPListItemCollection listItems;
     listItems = list.GetItems(queryRecurring);

Camel

CAML – Collaborative Application Markup Language.

CAML queries are used to retrieve SharePoint data. They look a bit like SQL queries expressed in XML, but in a prefix notation. For schema information on CAML queries, try: Query Schema, and for help with building queries, try: U2U CAML Query Builder and Execution Tool Released.

Gotcha: In CAML queries, you must use the name of the column, not the display name, and the real name can be hard to find, especially, if you renamed any columns. To find column names for any SharePoint list, open the list, edit the list settings, and scroll down until you find the list of columns.

Sharepoint Columns

Click on a column name to view its properties:

Sharepoint Column Editor

In the textbox at the right, the column name is “Start Time” - but that is the display name. The real column name is at the end of the URL(!). Look at the rightmost bit of the address bar to see “Field=EventDate”; and “EventDate” is the name we want.

Back to the CAML query. Recurring events … recur. For example, a seminar that occurs every week for 10 weeks, from 2 – 3 pm on Thursdays. These list items are created at runtime, not stored in the database (like an ordinary event), and a standard query will not return them. The DateRangesOverlap element must be used to find recurring events; it will return everything for the specified interval.

In this example, we’re choosing to look at one week at a time. Let’s take another look at that CAML query in an easier-to-read format:

CAML query

In the FieldRef elements of the query (which use the real column name), EventDate is the start date, EndDate the end date, and RecurranceID is not only the ID you’ll need for the hyperlink, but contains the start day and the time of the recurring event. An example of a RecurranceID for a recurring event: “4.0.2008-05-27T17:00:00Z”. In contrast, an example of a RecurranceID for an ordinary event: “7”.

A couple of attributes need to be set on the query also:

C#
queryRecurring.ExpandRecurrence = true;
queryRecurring.CalendarDate = day;

Set ExpandRecurrence to true so the recurring values get created. The CalendarDate property sets the actual date, which, combined with the CAML query’s “Week” value, will cause the query to grab all the entries in the week containing the “CalendarDate.

Processing the list items collection

Now that we have the item collection, we need to figure out if each item is an all-day, multi-day, or ordinary item; check if the item’s start date is in our date range, and if so, add it to our sorted list, calendarCells.

If it is a multi-day item, there will be only one occurrence, and we’ll need to add the other days manually; but, there may be one each week if the time span bridges the weekend. This raises the possibility of accidentally adding the same items twice, so we’ll save the uniqueId property in the Hashtable, processedListItems, so we can screen for already-added items. Each “cell” in the sorted list, calendarCells, will be 1 hour of a day.

C#
            bool multiday;
            bool allday;

            foreach (SPListItem item in listItems)
            {
                DateTime itemStartDate = (DateTime)item["EventDate"];
                DateTime itemEndDate = (DateTime)item["EndDate"];

                multiday = (itemEndDate - itemStartDate > 
                               new TimeSpan(1, 0, 0, 0)) ? true : false;
                allday = (itemEndDate - itemStartDate == 
                          new TimeSpan(23, 59, 0)) ? true : false;

                if (multiday)
                // is the calendar start day within
                // the Multi-Day span? if so, reset itemStartDay
                    if (day >= itemStartDate && day <= itemEndDate)
                        itemStartDate = day;

                if (!ItemIsInDateRange(itemStartDate, day, weeks))
                    continue;

                // Create key for assigning to gridrows.
                // Example: key for Thursday 8 am would be 08-4;
                //          key for Monday 3 pm would be 15-1
                // Example: key for an All Day item on Friday would be ..-5
                // Example: keys for a Multi-Day item spanning
                //          Thursday-Friday-Saturday:  ..-4 ..-5 ..-6
                string key = string.Empty;

                if (multiday || allday)
                    key = AllDayHour + "-" + 
                          ((int)itemStartDate.DayOfWeek).ToString();
                else
                {
                    key = itemStartDate.TimeOfDay.Hours.ToString() + "-" + 
                          ((int)itemStartDate.DayOfWeek).ToString();
                    if (key.Length < 4)
                        key = "0" + key;
                }

                string titleText = string.Empty;
                if (multiday || allday)
                    titleText = item["Title"].ToString();
                else
                    titleText = itemStartDate.ToShortTimeString() + 
                                " " + item["Title"].ToString();

                string linkUrl = web.Url + "/"
                        + item.Url.ToString().Substring(0, item.Url.LastIndexOf("/") + 1)
                        + "DispForm.aspx?ID=" + item.RecurrenceID.ToString();

                // Example for javascript tooltips:
                //string titleLink = "<a href=" + Quote(linkUrl)
                //    + " onmouseover=\"Tip('" + tip(item, multiday || allday) + 
                //      "')\" onmouseout=\"UnTip()\" >" + 
                //      titleText + "</a>";

                string titleLink = "<a href=" + Quote(linkUrl)
                    + " title=" + Quote(tip(item, multiday || allday)) + 
                      " >" + titleText + "</a>";

                string UniqueID = item.UniqueID + item.RecurrenceID;
                if (!processedListItems.Contains(UniqueID))
                {
                    AddCell(key, titleLink);

                    if (multiday)
                    {
                        // Multi-Day events appear as one list item; add remaining days 
                        DateTime thisEndDate = (StartDate.AddDays(NumberOfDays) < 
                          itemEndDate) ? StartDate.AddDays(NumberOfDays) : itemEndDate;
                        for (DateTime nextDay = itemStartDate.AddDays(1); 
                             nextDay <= thisEndDate; nextDay = nextDay.AddDays(1))
                        {
                            if (ItemIsInDateRange(nextDay))
                            {
                                key = AllDayHour + "-" + 
                                     ((int)nextDay.DayOfWeek).ToString();
                                AddCell(key, titleLink);
                            }
                        }
                    }
                    processedListItems.Add(UniqueID, null);
                }
            }  // foreach ListItem
        }  // foreach day in weeks
    } // using SPWeb web ...
}

Last bits

You’ll notice that tooltips (or screentips) here are handled by assigning them to the title attribute of the <a>tags, and there are commented-out lines nearby assigning JavaScript functions to the onmouseover and onmouseout events. If you prefer to use JavaScript tooltips, just un-comment these lines, and also the script-registering code in the OnLoad method, and a few lines in the tip method. (Don’t forget to upload the JavaScript file to the web server.) With JavaScript tooltips, you can include images, links, etc., in the tooltips.

Once the sorted list is built, we’re ready to build a DataTable, add matching columns to a GridView, and set the GridView’s DataSource to the table. A naming convention is used to tie the DataTable columns to the GridView columns. The Gridview columns are template fields, so HTML can be used.

One of the challenges of web part design is that there is no design surface, so you really can’t “see” what you’re doing. I found it very helpful to preview the GridView in a separate project -a sort of workbench- where I could just throw things on a design surface, set properties, play with the code, and spin it up fast.

Coda

Note: WebPart deployment is not covered here; for an introduction, see: Write Custom WebParts for SharePoint 2007.

Another note: You’ll probably need to refresh the project reference to the Sharepoint.dll, typically found here – C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI\Microsoft.SharePoint.dll.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)