In the new version of the Content By Type webpart, I added the option to rollup items into a calendar. In a later post I will describe how you can do that using the webpart. In this post I will show you how I did it in the code. When you start it all seems very easy, but there were a few things that caused me some headaches. I hope you don't run into them as well after reading this item.
Step 1 - Setting up the controls
I implemented all the calendar specific code in a special control:
public class SharePointCalendar : Control
In the CreateChildControls of my webpart, I just add this control to the collection:
protected override void CreateChildControls()
{ base.CreateChildControls();
SharePointCalendar calendar = new SharePointCalendar();
Controls.Add(calendar);
}
In the CreateChildControls of my SharePointCalendar class, I setup the SPCalendarView and add it to the controls collection:
private SPCalendarView _view;
/// <summary>
/// Create the SharePoint calendar. Uses the SharePoint SPCalendarView object.
/// </summary>
protected override void CreateChildControls()
{ base.CreateChildControls();
_view = new SPCalendarView();
_view.EnableViewState = true;
_view.Width = Unit.Percentage(100);
_view.DataSource = GetCalendarItems();
DataBind();
Controls.Add(_view);
}
You can find SPCalendarView in the Microsoft.SharePoint.WebControls namespace.
Step 2 - Adding data and testing
The data that will be displayed in the calendar is loaded in GetCalendarItems. In this case it is dummy data, but you will get the idea:
private SPCalendarItemCollection GetCalendarItems()
{ // Create a new collection for the calendar items
// This is an item with a start and end date.
SPCalendarItemCollection items = new SPCalendarItemCollection();
// Add the first dummy item
SPCalendarItem item = new SPCalendarItem();
item.StartDate = DateTime.Now;
item.EndDate = DateTime.Now.AddHours(1);
item.hasEndDate = true;
item.Title = "First calendar item";
item.DisplayFormUrl = "/News";
item.Location = "Utrecht";
item.Description = "This is the first test item in the calendar rollup";
item.IsAllDayEvent = false;
item.IsRecurrence = false;
item.CalendarType = Convert.ToInt32(SPCalendarType.Gregorian);
items.Add(item);
// Add the second item. This is an all day event.
SPCalendarItem item2 = new SPCalendarItem();
item2.StartDate = DateTime.Now.AddDays(-1);
item.hasEndDate = true;
item2.Title = "Second calendar item";
item2.DisplayFormUrl = "/News";
item2.Location = "Utrecht";
item2.Description = "This is the second test item in the calendar rollup";
item2.IsAllDayEvent = true;
item2.IsRecurrence = false;
item2.CalendarType = Convert.ToInt32(SPCalendarType.Gregorian);
items.Add(item2);
// return the collection
return items;
}
If you now build your webpart and add it to a page, it should look like this:
Step 3 - The ViewType
Looks nice and it indeed it very simple to do. But now you click one of the links "Week" or "Day" to switch to the daily or weekly view. That doesn't work. The webpart simply does what it does and it shows you the monthly view. If you look at the url however, you will see that the calendar view added a querystring parameter called "CalendarPeriod".
Add the following snippet to the CreateChildControls of the SharePointCalendar to make it work:
if (Page.Request.QueryString["CalendarPeriod"] != null)
{ switch (Page.Request.QueryString["CalendarPeriod"].ToString().ToLower())
{ case "day":
_view.ViewType = "day";
break;
case "week":
_view.ViewType = "week";
break;
case "timeline":
_view.ViewType = "timeline";
break;
default:
_view.ViewType = "month";
break;
}
}
The documentation for the ViewType property shows you the possible values, but please notice that you need to set them in lower case!. Not sure what the option "timeline" does. It looks like a normal monthly view if you use it.
Step 4 - The DisplayFormUrl problem
The property DisplayFormUrl expects are relative url to the item that you added to the calendar. In the sample above, I added a url to the News site. If you look at the properties of the hyperlink, you will see this link:
http://cbt/News?ID=
The calendar view automatically adds a querystring parameter ID to your urls. In this case this is not a problem. In the Content By Type webpart however, I add the same links that are generated by the out of the box ContentQuery webpart. These urls look like this:
http://cbt/_layouts/CopyUtil.aspx?Use=id&Action=dispform&ItemId=1&ListId={70fbb8db-c7b8-4b32-bf77-f61a13b3e0fc}&WebId={087c3dd8-b0a9-4960-9509-da8887d979b6}&SiteId={9fb94730-cc85-4924-957f-7504f415fe0f}
When you add "?ID=" to that url (that is what the calendar view does), your link will not work and the page will display an "Unknown error":
In the first approach I tried to fix the urls in javascript, but that didn't work. I was able to trim the "?ID=" bit, but something else kept on adding it back in. I ended up creating a page like the CopyUtil page. When I set the DisplayFormUrl property, I encode the Url that I want to redirect to and add that as a querystring parameter to my redirect page called "tstredirect.aspx":
string link = /_layouts/CopyUtil.aspx?Use=id&Action=dispform&ItemId=1&ListId={70fbb8db-c7b8-4b32-bf77-f61a13b3e0fc}&WebId={087c3dd8-b0a9-4960-9509-da8887d979b6}&SiteId={9fb94730-cc85-4924-957f-7504f415fe0f}string urlStart = SPContext.Current.Site.ServerRelativeUrl;
if (urlStart == "/")
urlStart = string.Empty;
link = string.Format("{0}/_layouts/tst/tstredirect.aspx?GoTo={1}", urlStart, System.Web.HttpUtility.UrlEncode(link));
item.DisplayFormUrl = link;
In that tstredirect page, I decode the querystring GoTo parameter and redirect to that url.
I am not very happy with that last approach, there should be a better way. If you have a suggestion, please let me know. So after all, it still is pretty easy to use the SPCalendarView in your webparts, if you are aware of these things. In the next post I will show you how I exactly implemented it in the Content By Type webpart.