Keep an Eye on Your Agenda

List Controls and Calendars Make It Easy

CoreCoder

LANGUAGES: C#

ASP.NET VERSIONS: 2.0

 

Keep an Eye on Your Agenda

List Controls and Calendars Make It Easy

 

By Dino Esposito

 

Hopefully after reading Enhancing the ASP.NET Calendar Control you realized that enhancing an ASP.NET Calendar control with the concept of busy days is not a mission-impossible task. All you must do is add a collection property to track appointments and tasks (collectively referred to as busy days ) and modify the rendering process of the Calendar control. You can do that in either of two ways: You can derive a new Calendar control and override the OnDayRender virtual method (what we did last month) or you can host a classic Calendar in the ASP.NET page and add a handler for the DayRender public event. The final effect is the same your code will be invoked each time the control is going to render the cell for a day. The event handler receives the date and checks it against the list of busy days. If a match is found, the cell that represents the day in the calendar s table is styled differently to denote the appointment.

 

The ASP.NET Calendar control, though, is just what its name suggests a user-friendly way to let users pick up dates in an input scenario. (Maybe Date Picker would have been a more descriptive name for the role it plays.) When you add the notion of appointments and tasks, you implicitly make the Calendar evolve toward a day planner and an agenda. In this case, the main issue is, how can I display multiple calendars to see my agenda for a set of consecutive months? Last month we simply built a user control that grouped together a bunch of extended calendar controls. We also built a small object model and added navigational capabilities to scroll through previous and successive months.

 

When you create a user control, though, you aggregate multiple individual controls in a monolithic container with a unique and all-encompassing programming interface. A new control for calendaring functionality will require a new markup structure entirely managed by the control itself exactly what many of the vendors out there do with their own calendar controls. A good compromise between simplicity, effectiveness of the solution, and time constraints is building a calendar control as a special case of a list control where repeated items are simply instances of the original Calendar control.

 

List Controls in ASP.NET

In ASP.NET, list controls are controls that display a list of data items through a fixed and immutable user interface. Popular examples of list controls are RadioButtonList, CheckBoxList, and, new in ASP.NET 2.0, BulletedList. In general, the main trait of list controls is the strict association between a data item and a block of markup: radio button, checkbox, bullet point. Internally, a list control has a fairly simple structure that is hard-coded in the ListControl base class.

 

Hence, a custom list control can be built by defining a control to repeat and initializing the control with the data contained in each bound data item. Most list controls also feature advanced layout capabilities, such as rendering in a given number of columns or rows.

 

Planning a CalendarList Control

The new CalendarList control will inherit from the ListControl base class and implement the IRepeatInfoUser interface. The base class provides a simple but effective and free mechanism for data binding and rendering. The additional interface is the standard ASP.NET interface for controls, with advanced layout capabilities such as multi-column rendering. In other words, the IRepeatInfoUser interface lies behind the popular capabilities of DataList and CheckBoxList controls that allow developers to change the final layout based on the value of three properties: RepeatLayout, RepeatDirection, and RepeatColumns:

 

public class CalendarList : ListControl, IRepeatInfoUser

{

   :

}

 

The CalendarList control features three public properties: BusyDays, NumberOfMonths, and StartDate. The first property indicates a start and an end date denoting an appointment or a planned task. Those days will be rendered in the client page according to a specific style. The BusyDays property is a collection property defined as follows:

 

public CalendarItemCollection BusyDays

{

 get

 {

   if (_busyDays == null)

   {

       _busyDays = new CalendarItemCollection();

       if (base.IsTrackingViewState)

           _busyDays.TrackViewState();

   }

     return _busyDays;

 }

}

 

A CalendarItem object is a custom class that identifies a planned activity through start and end dates, description, and a boolean flag to indicate whether it is a holiday/vacation or a regular workday. Details about this class can be found in the code download (see end of article for download details). If you read last month s column this should be nothing new these are the same classes I used for the last column. The CalendarItemCollection class implements the IStateManager interface to successfully save its data to the viewstate. The StartDate property indicates the first month to display, whereas the NumberOfMonths property sets how many child calendars make up the control.

 

Implementing IRepeatInfoUser

Figure 1 shows the implementation of the IRepeatInfoUser interface for the CalendarList control. The implementation is based on three additional properties (RepeatLayout, RepeatDirection, and RepeatColumns) and the override of the Render method.

 

bool IRepeatInfoUser.HasFooter

{

 get { return false; }

}

bool IRepeatInfoUser.HasHeader

{

 get { return false; }

}

bool IRepeatInfoUser.HasSeparators

{

 get { return false; }

}

int IRepeatInfoUser.RepeatedItemCount

{

 get { return this.NumberOfMonths; }

}

Style IRepeatInfoUser.GetItemStyle(ListItemType itemType,

 int repeatIndex)

{

 return null;

}

void IRepeatInfoUser.RenderItem(ListItemType itemType, int

 repeatIndex, RepeatInfo repeatInfo, HtmlTextWriter writer)

{

 Calendar ctl = ControlToRepeat;

 ctl.ApplyStyle(ControlStyle);

 ctl.WeekendDayStyle.MergeWith(WeekendDayStyle);

 ctl.DayHeaderStyle.MergeWith(DayHeaderStyle);

 ctl.TitleStyle.MergeWith(TitleStyle);

 ctl.SelectedDayStyle.MergeWith(SelectedDayStyle);

 ctl.OtherMonthDayStyle.MergeWith(OtherMonthDayStyle);

 int i = repeatIndex;

 ctl.ID = "CalendarItem" + i.ToString();

 ctl.VisibleDate = StartDate.AddMonths(i);

 // Fixed settings

 ctl.FirstDayOfWeek = FirstDayOfWeek.Monday;

 ctl.SelectionMode = CalendarSelectionMode.None;

 ctl.ShowNextPrevMonth = false;

 // End of fixed settings

 ctl.DayRender += new DayRenderEventHandler(ctl_DayRender);

 ctl.RenderControl(writer);

}

Figure 1: Implementing the IRepeatInfoUser interface.

 

Here s the required format of the Render method when a list control implements the IRepeatInfoUser interface:

 

protected override void Render(HtmlTextWriter writer)

{

 if (NumberOfMonths > 0)

 {

   RepeatInfo ri = new RepeatInfo();

   Style controlStyle = (base.ControlStyleCreated ? base.

    ControlStyle : null);

   ri.RepeatColumns = RepeatColumns;

   ri.RepeatDirection = RepeatDirection;

   ri.RepeatLayout = RepeatLayout;

   ri.RenderRepeater(writer, this, controlStyle, this);

 }

}

 

The RepeatInfo class used in the code snippet is a little-known class defined in the ASP.NET framework. As you can guess, the NumberOfMonths property poses an upper bound to the number of items the engine has to render. In general, the items to render are the contents of the Items collection, a property on the ListControl base class. In this case, the Items property won t be used and the items to render come from the contents of the BusyDays collection. The control you get from this article doesn t support data binding, but it is really prearranged for that. To finish the saga of Calendar controls I ll demonstrate data binding in my next column.

 

Styling the CalendarList Control

The toughest challenge I ve found with the CalendarList control regards styles. For rich controls like Calendar the building block of the CalendarList control styles and formatting are essential. Adding styles to the native Calendar control is a breeze: simply select the control in the Visual Studio 2005 IDE, run the auto-format wizard, and go. If you want to do it programmatically, access the countless style properties of the control and embellish it at your leisure.

 

The CalendarList control is not a Calendar control and, as such, it doesn t inherit all of its style properties. Is replicating style properties the only way out? What about themes? Themes would be a great trick to style controls without significantly enhancing their source code. When the ASP.NET page has attached a theme that styles the Calendar control, all child instances of the Calendar control within the page inherit the settings. Unfortunately, this works if, and only if, the Calendar control is a direct child of the page. It doesn t work if the control is aggregated in a user control or is defined within the tree of a page control. Theming doesn t even work if you style the Calendar control and then use in your pages a control that derives directly from Calendar.

 

In brief, the ASP.NET theme parser uses an exact-type match algorithm and simply skips child and derived controls. To cut a long story short, to style all child calendars you must replicate on the CalendarList class any visual and style properties you want to be able to customize, either programmatically or through themes. Here s the list of supported style properties:

  • DayHeaderStyle
  • DayStyle
  • HolidayStyle
  • OtherMonthDayStyle
  • SelectedDayStyle
  • TitleStyle
  • WeekendDayStyle

 

With the exception of HolidayStyle, all properties are simply proxies for identical Calendar properties. Here s a sample implementation:

 

public TableItemStyle HolidayStyle

{

 get

 {

   if (this._holidayStyle == null)

   {

     this._holidayStyle = new TableItemStyle();

     if (base.IsTrackingViewState)

     {

          ((IStateManager)_holidayStyle).TrackViewState();

     }

   }

   return this._holidayStyle;

 }

}

 

It is worth noticing that style properties take care of saving state to the viewstate themselves. The TableItemStyle class used to style the various elements of the calendar implements the IStateManager interface through which the class manages the viewstate. Styles are applied during the rendering stage, as you can see in Figure 1, specifically in the body of the RenderItem method.

 

Repeating the Calendar Control

Generally, list controls maintain an instance of the control to repeat for each bound item. The CalendarList control repeats for the specified number of times an instance of the Calendar control occurs. This control is cached in internal, private properties, as shown here:

 

private Calendar ControlToRepeat

{

 get

   {

   if (_controlToRepeat == null)

   {

      _controlToRepeat = new Calendar();

      _controlToRepeat.EnableViewState = this.EnableViewState;

   }

     return _controlToRepeat;

 }

}

 

Needless to say, you can use and repeat any control tree you like; it doesn t necessarily have to be a single control. Finally, you might want to control its viewstate explicitly and disable it when disabled on the parent control. In the client page, use the markup shown in Figure 2.

 

 NumberOfMonths="6" RepeatColumns="3" RepeatDirection="Horizontal">

  

      

          Description="Trip to Montecarlo" IsHoliday="False" />

     

          Description="Easter at home" IsHoliday="True" />

      

          Description="Trip to Acapulco" IsHoliday="False"  />

     

          Description="Trip to Las Vegas" IsHoliday="False"  />

      

          Description="Trip to Amsterdam" IsHoliday="False"  />

      

          Description="TechEd USA" IsHoliday="False"  />

      

          Description="Summer vacation" IsHoliday="True" />

  

Figure 2: Use this markup in the client page.

 

Figure 3 shows the control in action. In Visual Studio 2005, you can take advantage of the designer to set the number of months displayed, as well as the layout (see Figure 4). As you may have noticed, the markup doesn t include any formatting; all style attributes are assigned via themes.

 


Figure 3: The CalendarList control in action.

 


Figure 4: The CalendarList control in the Visual Studio 2005 IDE.

 

Conclusion

There are many controls that provide calendaring capabilities; some free, some not. The control presented in this article doesn t claim to compete with commercial products, but it offers a quick solution to a common demand and it comes with full source that can be easily adapted, extended, and personalized. The CalendarList control provided in this article doesn t provide binding capabilities and doesn t support navigation. Next month, I ll complete the saga of calendars with data binding, and more.

 

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 mailto: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