Bind Data to Your Calendar

Your Monthly Schedule at Your Fingertips

Dino Esposito

October 30, 2009

9 Min Read
ITPro Today logo

With this article, the saga of the Calendar controlreaches its end. In the first installment, I explored ways to extend the baseASP.NET Calendar control with simple calendaring functionalities (seeEnhancingthe ASP.NET Calendar Control. It should be noted that, despite the name,the ASP.NET Calendar control provides essentially date-picking capabilities.The support for tasks (referred to here as busy days ) was taken to the nextlevel in last month s article by a multi-month view of the calendar (see Keepan Eye on Your Agenda). Basically, I created a new list control that workedby repeating the Calendar control for the specified number of months, startingfrom a given date. Figure 1 shows the result I obtained. To top off thesubject, this month I m adding to the CalendarList control data bindingcapabilities so you can add busy days through a data source control or anin-memory collection.

 


Figure 1: The CalendarList controlin action.

 

A CalendarList Refresher

The CalendarList control features a BusyDays propertyimplemented as a custom collection of CalendarItem objects. Each busy dayconsists of a start and an end date, plus a description and a Boolean flag toindicate whether it s a holiday or a business appointment. The control also supportsa number of styles the same set of styles of a regular Calendar control, suchas days, weekend days, days of another month, title, and a few more. Styleproperties are serialized to the viewstate using a custom algorithm implementedby overriding the pair of methods SaveViewState and LoadViewState. Here s thetypical markup for a CalendarList control:

                   :    

You can add busy days either declaratively, as shownabove, or programmatically:

CalendarItem item1 = new CalendarItem();item1.StartDate = new DateTime(2007, 7, 23);item1.EndDate = new DateTime(2007, 7, 31);item1.IsHoliday = true;item1.Description = "Summer vacation";BusyDays.Add(item1); 

The ideal location for this code is the Page_Load eventhandler; it s better if it s in a branch that executes only when the page isloading for the first time. So is data binding really needed to further enhancethe control?

Data binding won t make the CalendarList control morepowerful in terms of functionalities. In this case, data binding will simplymake the control easier to use and with much less code to write on your side.

 

Adding Data Binding Capabilities

Data binding support consists of some code added to thecontrol that populates the BusyDays collection from the contents of a bound andenumerable data source. Basically, the aforementioned code snippet will beincorporated in the CalendarList control and activated when either theDataSource or DataSourceID property is set. By setting any of these properties,you pass data to the control that will be used to fill the BusyDays collection.The advantage of data binding is that to trigger the population of the control,all you must do is set one property.

In ASP.NET 2.0, the internal object model of servercontrols has been reworked to expose a few more virtual methods that derivedcontrols can easily override to implement additional features, such as databinding.

The CalendarList control inherits from ListControl, which,in turn, inherits from a new base class named DataBoundControl. Specificallydesigned to provide some core code for data-bound controls, theDataBoundControl class supplies a virtual method named PerformDataBinding:

protected virtual void PerformDataBinding( IEnumerable dataSource) 

This method receives an enumerable object typically, acollection object and does whatever is required to update the state of thecontrol with the bound data. All you must do to add data binding capabilitiesto a control that derives from ListControl is override the PerformDataBindingmethod.

Because CalendarList inherits from ListControl, itautomatically inherits the DataSource and DataSourceID properties:

public virtual object DataSourcepublic virtual string DataSourceID

Figure 2 shows the source code of the PerformDataBindingmethod, as implemented by the CalendarList control.

protected override void PerformDataBinding( IEnumerable dataSource){   base.PerformDataBinding(dataSource);   if (dataSource != null)   {       if (!AppendDataBoundItems)       {           BusyDays.Clear();       }       string startField = StartDateField;       string endField = EndDateField;       string descField = DescriptionField;       string holidayField = HolidayField;       foreach (object o in dataSource)       {           CalendarItem item = new CalendarItem();           if (startField.Length > 0)           {               item.StartDate = (DateTime)                 DataBinder.GetPropertyValue(o, startField);           }           if (endField.Length > 0)           {               item.EndDate = (DateTime)                DataBinder.GetPropertyValue(o, endField);           }           if (descField.Length > 0)           {               item.Description = (string)                DataBinder.GetPropertyValue(o, descField);           }           if (holidayField.Length > 0)           {               item.IsHoliday = (Boolean)                DataBinder.GetPropertyValue(o,                holidayField);           }           BusyDays.Add(item);       }   }}

Figure 2: Implementingdata binding capabilities.

The data binding operation is essentially centered around aloop that copies to the BusyDays property each element in the bound enumerabledata source. A few properties are involved in the preparation of the loop, oneof which is AppendDataBoundItems. Defined on the base class,AppendDataBoundItems is a Boolean property that determines whether the controlshould append data-bound items to any items defined declaratively in thecontrol s markup.

Data binding maps fields in the bound data to propertiesof the objects in the BusyDays collection. The BusyDays collection consists ofCalendarItem objects with properties such as StartDate, EndDate, andDescription. At the end of a data binding operation, each CalendarItem objecthas its bindable properties set to the values of fields in the bound data. Themapping is obtained through a set of control properties that identify whichexternal fields will be mapped to which internal property. The CalendarListcontrol defines four properties:

public string StartDateField { get; set; }public string EndDateField { get; set; }public string DescriptionField { get; set; }public string HolidayField { get; set; }

These properties are used in Figure 2 to read values fromthe bound data source. You use the GetPropertyValue static method on theDataBinder class on purpose:

item.StartDate = (DateTime) DataBinder.GetPropertyValue( o, StartDateField);

The preceding code snippet maps the value of the specifiedfield to the StartDate property of the nthcalendar item. You typically set the StartDateField property in the controlmarkup:

 

The mapping properties are plain string properties,implemented as shown here:

public string StartDateField{   get   {       object o = ViewState["StartDateField"];       if (o == null)           return String.Empty;       else           return (string)o;   }   set   {       ViewState["StartDateField"] = value;   }}

To finalize data binding in a server control, you musttweak the viewstate management to include the data bindable collection property(the BusyDays property). From a control s perspective, viewstate managementessentially means overriding two methods, SaveViewState and LoadViewState. Figure3 shows the code in detail.

protected override object SaveViewState(){    object[] styles = new object[9];   styles[0] = base.SaveViewState();   styles[1] = ((IStateManager)WeekendDayStyle).SaveViewState();   styles[2] = ((IStateManager)TitleStyle).SaveViewState();   styles[3] = ((IStateManager)DayHeaderStyle).SaveViewState();   styles[4] = ((IStateManager)SelectedDayStyle).SaveViewState();   styles[5] = ((IStateManager)OtherMonthDayStyle).SaveViewState();   styles[6] = ((IStateManager)HolidayStyle).SaveViewState();   styles[7] = ((IStateManager)DayStyle).SaveViewState();   // Added for data binding   styles[8] = ((IStateManager)_busyDays).SaveViewState();   return styles;}protected override void LoadViewState(object savedState){   if (savedState == null)       return;   object[] data = (object[]) savedState;   if (data[0] != null)       base.LoadViewState(data[0]);   if (data[1] != null)        ((IStateManager)WeekendDayStyle).LoadViewState(data[1]);   if (data[2] != null)        ((IStateManager)TitleStyle).LoadViewState(data[2]);   if (data[3] != null)        ((IStateManager)DayHeaderStyle).LoadViewState(data[3]);   if (data[4] != null)        ((IStateManager)SelectedDayStyle).LoadViewState(data[4]);   if (data[5] != null)        ((IStateManager)OtherMonthDayStyle).LoadViewState(data[5]);   if (data[6] != null)        ((IStateManager)HolidayStyle).LoadViewState(data[6]);   if (data[7] != null)        ((IStateManager)DayStyle).LoadViewState(data[7]);   // Added for data binding   if (data[8] != null)        ((IStateManager)BusyDays).LoadViewState(data[8]);}

Figure 3: Viewstatemanagement in a data-bound control.

 

DataSource and DataSourceID

Data binding for a CalendarList control ultimately meansthat a developer can specify the contents of the BusyDays collection througheither the DataSource or DataSourceID property.

The DataSource property lets you specify the data sourceas an IEnumerable-based object. Note that through the DataSource property youestablish only a logical link. In no way does property assignment result in anyunderlying operation until you explicitly order to bind the data. You bind datato a control by calling the DataBind method. When the method executes, thecontrol actually loads data from the associated data source, evaluates the data-boundproperties (if any), and generates the markup to reflect changes.

Introduced with ASP.NET 2.0, the DataSourceID propertygets or sets the ID of the data source component from which a data-boundcontrol retrieves its data. This property is the point of contact betweenASP.NET 2.0 data-bound controls and the new family of data source controls likeSqlDataSource and ObjectDataSource. The two properties are mutually exclusive.If both are set, you get an invalid operation exception at run time. Note, though,that you also get an exception if DataSourceID is set to a string that doesn tcorrespond to an existing data source control.

The following code shows how to run a query, get somerecords, and bind them to the CalendarList control:

protected void Page_Load(object sender, EventArgs e){   if (!IsPostBack)   {      IEnumerable data = RunQuery();      CalendarList1.DataSource = data;      CalendarList1.DataBind();   }}

Endowed with a working binding mechanism, the CalendarListcontrol can be used to display busy days throughout the year, the quarter, orwhatever period of time you wish. What if you want to give your users thepossibility to navigate through the calendar? By default, all child calendarcontrols don t allow selection and navigation. Subsequently, this feature mustbe provided otherwise.

 

An individual Calendar control may optionally supply twolink buttons for users to navigate to the previous and next month. But what aboutwhen you have multiple calendars in a single control? Should you havenavigation enabled on all of them? Just one? And, in this case, which one?

Whichever route you take at the design level mightencounter objections. So I decided to keep it at a lower level and asflexible as possible. The CalendarList control has added a couple of methods, GetNextand GetPrevious, and a new integer property, NextPrevStep:

public short NextPrevStep{   get   {       object o = ViewState["NextPrevStep"];       if (o == null)           return this.NumberOfMonths;       else           return (short) o;   }   set   {       ViewState["NextPrevStep"] = value;   }}

As you can see, the NextPrevStep property defaults to thevalue of the NumberOfMonths property. This means that, if you configured yourcalendar list to a six-month view, each time you invoke GetNext/GetPrevious,the calendar will display the next/previous six months starting from the monthfeatured in the first child calendar. Here s the implementation of the twomethods:

public void GetNext(){   this.StartDate = this.StartDate.AddMonths(    this.NextPrevStep);}public void GetPrevious(){   this.StartDate = this.StartDate.AddMonths(    -1 * this.NextPrevStep);}

With this additional equipment you can add to a host page acouple of buttons (or any other kind of markup) and attach the following codeto the click handlers:

protected void Button1_Click(object sender, EventArgs e){   CalendarList1.GetPrevious();}protected void Button2_Click(object sender, EventArgs e){   CalendarList1.GetNext();}

Figure 4 shows a sample that implements this feature.

 


Figure 4: Navigating through yourschedule using the CalendarList control.

 

Conclusion

Calendar controls are a key tool for many ASP.NET pages. Theoriginal ASP.NET 2.0 Calendar control provides some great (and free) features,but properly extended with list, navigation, and binding capabilities, it becomesan even more powerful and helpful tool. This article delivers a fullyfunctional CalendarList control to quickly display and update your agendaonline.

The sample code accompanyingthis article is available for download.

 

Dino Esposito is amentor with Solid Quality Mentors who specializes mainly in ASP.NET and AJAXsolutions. He s the author of Introduction toASP.NET AJAX Extensions (Microsoft Press, May 2007) and wrote the twovolumes of Programming Microsoft ASP.NET 2.0,also for Microsoft Press. Late breaking news is available at http://weblogs.asp.net/despos.

 

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like