Skip navigation

Bind Data to Your Calendar

Your Monthly Schedule at Your Fingertips

With this article, the saga of the Calendar control reaches its end. In the first installment, I explored ways to extend the base ASP.NET Calendar control with simple calendaring functionalities (seeEnhancing the 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 next level 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 worked by repeating the Calendar control for the specified number of months, starting from a given date. Figure 1 shows the result I obtained. To top off the subject, this month I m adding to the CalendarList control data binding capabilities so you can add busy days through a data source control or an in-memory collection.

 


Figure 1: The CalendarList control in action.

 

A CalendarList Refresher

The CalendarList control features a BusyDays property implemented as a custom collection of CalendarItem objects. Each busy day consists of a start and an end date, plus a description and a Boolean flag to indicate whether it s a holiday or a business appointment. The control also supports a number of styles the same set of styles of a regular Calendar control, such as days, weekend days, days of another month, title, and a few more. Style properties are serialized to the viewstate using a custom algorithm implemented by overriding the pair of methods SaveViewState and LoadViewState. Here s the typical markup for a CalendarList control:



    

       

        :

   

 

You can add busy days either declaratively, as shown above, 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 event handler; it s better if it s in a branch that executes only when the page is loading for the first time. So is data binding really needed to further enhance the control?

Data binding won t make the CalendarList control more powerful in terms of functionalities. In this case, data binding will simply make 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 the control that populates the BusyDays collection from the contents of a bound and enumerable data source. Basically, the aforementioned code snippet will be incorporated in the CalendarList control and activated when either the DataSource 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 server controls has been reworked to expose a few more virtual methods that derived controls can easily override to implement additional features, such as data binding.

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

protected virtual void PerformDataBinding(

 IEnumerable dataSource) 

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

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

public virtual object DataSource

public virtual string DataSourceID

Figure 2 shows the source code of the PerformDataBinding method, 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: Implementing data binding capabilities.

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

Data binding maps fields in the bound data to properties of the objects in the BusyDays collection. The BusyDays collection consists of CalendarItem objects with properties such as StartDate, EndDate, and Description. At the end of a data binding operation, each CalendarItem object has its bindable properties set to the values of fields in the bound data. The mapping is obtained through a set of control properties that identify which external fields will be mapped to which internal property. The CalendarList control 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 from the bound data source. You use the GetPropertyValue static method on the DataBinder class on purpose:

item.StartDate = (DateTime) DataBinder.GetPropertyValue(

 o, StartDateField);

The preceding code snippet maps the value of the specified field to the StartDate property of the nth calendar item. You typically set the StartDateField property in the control markup:

 

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 must tweak the viewstate management to include the data bindable collection property (the BusyDays property). From a control s perspective, viewstate management essentially means overriding two methods, SaveViewState and LoadViewState. Figure 3 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: Viewstate management in a data-bound control.

 

DataSource and DataSourceID

Data binding for a CalendarList control ultimately means that a developer can specify the contents of the BusyDays collection through either the DataSource or DataSourceID property.

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

Introduced with ASP.NET 2.0, the DataSourceID property gets or sets the ID of the data source component from which a data-bound control retrieves its data. This property is the point of contact between ASP.NET 2.0 data-bound controls and the new family of data source controls like SqlDataSource 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 t correspond to an existing data source control.

The following code shows how to run a query, get some records, 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 CalendarList control can be used to display busy days throughout the year, the quarter, or whatever period of time you wish. What if you want to give your users the possibility to navigate through the calendar? By default, all child calendar controls don t allow selection and navigation. Subsequently, this feature must be provided otherwise.

 

Adding Navigation Capabilities

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

Whichever route you take at the design level might encounter objections. So I decided to keep it at a lower level and as flexible as possible. The CalendarList control has added a couple of methods, GetNext and 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 the value of the NumberOfMonths property. This means that, if you configured your calendar list to a six-month view, each time you invoke GetNext/GetPrevious, the calendar will display the next/previous six months starting from the month featured in the first child calendar. Here s the implementation of the two methods:

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 a couple of buttons (or any other kind of markup) and attach the following code to 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 your schedule using the CalendarList control.

 

Conclusion

Calendar controls are a key tool for many ASP.NET pages. The original ASP.NET 2.0 Calendar control provides some great (and free) features, but properly extended with list, navigation, and binding capabilities, it becomes an even more powerful and helpful tool. This article delivers a fully functional CalendarList control to quickly display and update your agenda online.

The sample code accompanying this article is available for download.

 

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

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish