Friday, October 7, 2011
Databound Schedule Controls
Table of Contents
Introduction
These are 2 free controls,ScheduleCalendar
and ScheduleGeneral
, designed to show scheduled events in the form of a table. They are simple versions of the so-called Gantt chart. They don't have advanced features such as dependencies and milestones, but on the other hand, they use templated databinding, so it's up to you, the developer, what you want to show. The controls can be used for a broad variety of applications: time tables, resource usage planners, calendars, event schedulers, activities, reservations, sequences, project management, etc... See the demos for some examples.
The
ScheduleCalendar
control for instance will turn this data: StartTime | EndTime | EventDate | Task |
9:00 | 11:00 | 3/8/2004 | History |
9:00 | 10:00 | 3/9/2004 | Math |
10:00 | 11:00 | 3/9/2004 | Biology |
11:00 | 12:00 | 3/9/2004 | History |
9:00 | 10:00 | 3/10/2004 | Geology |
10:00 | 12:00 | 3/10/2004 | Math |
9:00 | 10:00 | 3/11/2004 | Economy |
10:00 | 12:00 | 3/11/2004 | Literature |
9:00 | 12:00 | 3/12/2004 | Sports |
9:00 | 11:00 | 3/15/2004 | History |
9:00 | 10:00 | 3/16/2004 | Math |
10:00 | 11:00 | 3/16/2004 | Biology |
11:00 | 12:00 | 3/16/2004 | History |
9:00 | 10:00 | 3/17/2004 | Geology |
10:00 | 12:00 | 3/17/2004 | Math |
9:00 | 10:00 | 3/18/2004 | Economy |
10:00 | 12:00 | 3/18/2004 | Literature |
9:00 | 12:00 | 3/19/2004 | Sports |
The other control is the
ScheduleGeneral
. Here you can provide your own titles (you may think of people, resources or location names). Here's an example with TV stations: Automatically, overlapping events will cause extra rows or columns to be added to the table, enabling them to be shown correctly.
Overlapping items:
The controls are written in VB.NET, but they can be used in any ASP.NET application. You may need to slightly adapt some of the sample code below to match the language used in your application.
The controls require at least ASP.NET 2.0 and support declarative databinding and smart tags. They come with a documentation file "documentation2.chm".
Try the online demo.
License
The controls are free and open source under the LGPL license.Installation
Installing the Controls
You can install the controls in the standard way:- Create a "bin" folder inside the application root of your website (if it's not already there)
- Copy the assembly file schedule2.dll into the bin folder
In order to support IntelliSense in HTML View in Visual Studio (and to avoid some compiler warnings):
- Copy the schedule.xsd file to the folder
C:\Program Files\Microsoft Visual Studio x\Common7\Packages\schemas\xml
- Modify the
<body>
tag of any new web form as follows:<body xmlns:cc1="urn:http://www.rekenwonder.com/schedule">
wherecc1
is the TagPrefix of the schedule control (the one assigned in the "Register
" directive).
Installing the Demos
I added a set of simple demo files with inline code, that can be used with Visual Studio.Unzip the zip file with the demos to a subfolder of the wwwroot folder. Then, either create a virtual directory for this folder in IIS, or copy the schedule2.dll file to the bin folder in wwwroot. For the demo files, that's all you have to do. Just call the first demo page through the address http://localhost/<foldername>/demo1.aspx in your browser.
Using the Controls
Adding the Controls to your Page
There are 2 ways to add the controls to your page:- Drag the control from the toolbox onto the page (if it was installed on the toolbox). Be careful to select the correct type:
ScheduleCalendar
for a weekly calendar schedule,ScheduleGeneral
for other schedules. - Add the code manually. Add this line to the top of your page:
<%@ Register TagPrefix="rw" Namespace="rw" Assembly="Schedule2" %>
Then, add a tag like this where you want the schedule to be displayed:<rw:ScheduleGeneral id="Schedule1" runat="server" >
Of course, for the calendar version, you use
<ItemTemplate>
</ItemTemplate>
</rw:ScheduleGeneral>ScheduleCalendar
.
If you're using code-behind, you should also add a reference to theSchedule
instance in your page class, like this:Protected WithEvents Schedule1 As rw.ScheduleGeneral
In the code-behind page, also add this import statement:Imports rw
Setting the Control's Properties
Make sure you use the appropriate settings.You can use any
DataSourceControl
for data binding. Use the smart tag menu to configure this type of data source.If you don't use declarative databinding, the
DataSource
property should be either an IEnumerable
or an IListSource
. You can't databind to a DataReader
, since the control must be able to sort the data internally. For each item on the schedule, the data source should provide a single record containing a start value, an end value, and custom data (typically a description of the task or event). For the
ScheduleCalendar
control when TimeFieldsContainDate=false
, you also need a date value. For the ScheduleGeneral
control, you also need a title value (for display on column/row headers). You need to set 3 properties (similar to the 2 properties
DataTextField
and DataValueField
that you have to set for a bound ListBox
or CheckBoxList
): ScheduleCalendar
StartTimeField
: The database field (column) containing the start of the eventEndTimeField
: The database field containing the end of the eventDateField
: The database field providing the dates. This field should be of type "Date
" (exception: whenTimeFieldsContainDate=true
this field is ignored, see below). In a vertical layout, this field will be used for column titles. In a horizontal layout, it will be used for row titles.
ScheduleGeneral
DataRangeStartField
: The database field (column) containing the start of the eventDataRangeEndField
: The database field containing the end of the eventTitleField
: The database field providing the titles. You can think of some useful fields for titles: people names, locations, classrooms, resources, etc...) In a vertical layout, this field will be used for column titles. In a horizontal layout, it will be used for row titles.
TimeFieldsContainDate=true
for the ScheduleCalendar
control, they should contain date and time information, and when FullTimeScale=True
, they should contain at least time information. Note that there is no property to indicate which field contains the "
Task
" or "Event
" information. Actually, this information can be anything: a field, a combination of multiple fields, etc... This is possible since the "Task
" data is provided through a databinding expression in the Item
template. Its simplest form would be something like this:<ItemTemplate>
<%# Eval("taskfieldname") %>
</ItemTemplate>
Optionally, you can set the ItemStyleField
. When its value is not blank, the database field with this name will be used as a CSS style class for the item. This lets you set the style for each item separately from the data source. Then, there's the
FullTimeScale
property. When false
(the default value), only range values occurring in the data source are shown. When true
, a continuous time scale is displayed (like the Outlook calendar). For the ScheduleGeneral
control, this requires the DataRangeStartField
and DataRangeEndField
fields in the data source to be of type Date
/Time
. The image below shows the same item (Lunch from 12:30 to 13:30) in both cases:
FullTimeScale = false
|
FullTimeScale = true and TimeScaleInterval = 15
|
FullTimeScale
is true
, there are three additional properties that you can set: TimeScaleInterval
: An integer value giving the number of minutes for the interval (default=60)StartOfTimeScale
: A timespan value setting the start time of the time scale (default=8:00)EndOfTimeScale
: A timespan value setting the end time of the time scale (default=17:00). The highest value allowed here is 24:00 (midnight), which should be entered as "1.00:00" (one day, zero hours and minutes).
IncludeEndValue
and ShowValueMarks
. When IncludeEndValue
is True
, the row with the end value will be included in the event. Sometimes, this will result in a more logical presentation. However, when the items are adjacent (for instance: one event ends at 10:00 AM, and another starts at the same time), it's better to set IncludeEndValue
to False
. Otherwise, the items will be shown as overlapping. In this case, you can also set ShowValueMarks
to True
, which will add marks indicating the values, and the values will be shown in the middle of the item. The default value is False
for both. The image below shows the same item (Lunch from 12:30 to 13:30) in all cases:
IncludeEndvalue = False and ShowValueMarks = False
12:30 | Lunch from 12:30 to 13:30 |
13:00 | |
13:30 | |
14:00 |
IncludeEndValue = True (ShowValueMarks is ignored)
12:30 | Lunch from 12:30 to 13:30 |
13:00 | |
13:30 | |
14:00 |
IncludeEndValue = False and ShowValueMarks = True
12:30 | ||
Lunch from 12:30 to 13:30 | ||
13:00 | ||
13:30 | ||
14:00 | ||
EnableEmptySlotClick
property to true
, and handle the OnEmptySlotClick
event. Optionally, set a tooltip for empty slots with the EmptySlotTooltip
property. For samples, see demo1
and demo2
.For the
ScheduleCalendar
control, you should set these additional properties: NumberOfDays
: The number of days to show at a time. The default value is7
, making it a weekly calendar.NumberOfRepetitions
: The number of times to repeat the number of days (the default setting is 1 repetition). WhenNumberOfDays=7
, this is the number of weeks that will be shown in the control.StartDay
: The day of the week to start the calendar. This property is only used when theNumberOfDays
property is set to7
. The default value isMonday
.StartDate
: Date to start displaying events. If this date is not on the same day of the week as theStartDay
property, the control will start displaying on the same day prior to the chosen date. The default value is today's date. See the demos to learn how to set today's date programmatically.TimeFieldsContainDate
: Whenfalse
(the default), theDateField
property is used to extract dates for each item. This means that items must start and end on the same day. If some of your items span midnight, you should setTimeFieldsContainDate
totrue
, and you should provide a full date and time in both theStartTimeField
andEndTimeField
fields. In this case, theDateField
property is ignored.
ScheduleGeneral
control has two additional properties: AutoSortTitles
: Whentrue
, the titles are sorted automatically, even when they are not sorted in the data source. Whenfalse
, you may provide your own sorting order for the titles, but make sure that the items with the same titles are grouped together, and that for each title, the items are sorted onDataRangeStartField
first and onDataRangeEndField
next. (For example: If you want to sort on a field called "SortOrder
", theDataRangeStartField
is "StartTime
", and theDataRangeEndField
is "EndTime
", use the sorting expression "ORDER
BY SortOrder ASC, StartTime ASC, EndTime ASC")
. The default value forAutoSortTitles
istrue
.SeparateDateHeader
: Whentrue
, an extra range of cells will be added to group all events of the same date. This requiresDataRangeStartField
andDataRangeEndField
to be of typeDateTime
. The default isFalse
.
SeparateDateHeader=False
|
SeparateDateHeader= True
|
ScheduleCalendar
and ScheduleGeneral
controls, you can easily switch between a vertical or a horizontal layout with the Layout
property, even at run time.Providing Template Content
The next step is to add the content of the templates.You can right-click on the control to edit the templates, or you can create the templates in HTML view. Also look at the templates in the demo pages for samples.
First of all, you should provide the
ItemTemplate
<ItemTemplate>
<%# Eval("taskfieldname") %>
</ItemTemplate>
If you want, you can combine information from several fields in the database. You can also add controls, images, just like you can in a Repeater
or DataList
control. The other templates are for the headers. Especially in the case of a time and date fields, you may want to format the content for proper display.
For the
ScheduleGeneral
control:TitleTemplate
<TitleTemplate>
<%# Container.DataItem %>
</TitleTemplate>
RangeHeaderTemplate
<RangeHeaderTemplate>
<%# Convert.ToDateTime(Container.DataItem).ToShortTimeString() %>
</RangeHeaderTemplate>
DateHeaderTemplate
(only used whenSeparateDateHeader
is set totrue
)
<DateHeaderTemplate>
<%# Convert.ToDateTime(Container.DataItem).ToShortDateString() %>
</DateHeaderTemplate>
For the ScheduleCalendar
control:DateTemplate
<TitleTemplate>
<%# Convert.ToDateTime(Container.DataItem).ToShortDateString() %>
</TitleTemplate>
TimeTemplate
<RangeHeaderTemplate>
<%# Convert.ToDateTime(Container.DataItem).ToShortTimeString() %>
</RangeHeaderTemplate>
If you don't bind to a data source declaratively, don't forget to call the DataBind()
method. Without this call, nothing will be shown.Finally, there's the
EmptyDataTemplate
. Optionally, you can use this template to provide a message to be shown when no data is found in the data source. This template is not used by the ScheduleCalendar
control when FullTimeScale=True
.You can bind the controls to a
DataSourceControl
, just like you can with a GridView
control. Use the smart tag menu and the "Select Data Source" wizard.The
Schedule
control can also be used in Master/Detail scenarios. Typically, you can link it to a FormView
or a DetailsView
control, which allows for easy editing of data items. Make sure to include a primary key field in the data source for the Schedule
control, and set the DataKeyNames
property to the name of that primary key field.After adding the
FormView
or the DetailsView
control, use the "Select Data Source" wizard. Use the WHERE
button to link the control to the Schedule
control.Upgrading from a Version Prior to 1.5
If you have projects working with a version prior to 1.5, you need to upgrade them.The
Schedule
class has been removed from the assembly, from now on you'll have to use either ScheduleCalendar
or ScheduleGeneral
. If your project contains a
Schedule
control with DisplayMode=Calendar
: - Change the control's type from
Schedule
toScheduleCalendar
(just change the type manually in the page and in the codebehind file if you use one) - Rename the
TitleTemplate
into aDateTemplate
(with the same content) - Rename the
RangeHeaderTemplate
into aTimeTemplate
(with the same content) - Rename the
TitleField
property into aDateField
property (with the same value) - Rename the
RangeHeaderStartField
property into aStartTimeField
property (with the same value) - Rename the
RangeHeaderEndField
property into anEndTimeField
property (with the same value) - Rename the
UseTitleFieldAsDate
property into aTimeFieldsContainDate
property (with the opposite value, changetrue
intofalse
andfalse
intotrue
) - Rename the
TitleStyle
tag toDateStyle
, or remove it when there is aDateStyle
tag already - Rename the
RangeHeaderStyle
tag toTimeStyle
, or remove it when there is aTimeStyle
tag already
Schedule
control with DisplayMode=General
: - Change the control's type from
Schedule
intoScheduleGeneral
(just change the type manually in the page and in the codebehind file if you use one) - If some properties (such as
Weeks
,StartDate
andUseTitleFieldAsDate
) that were not needed forDisplayMode=General
were given a value anyway, remove them.
Demo Pages
Download the demo pages for samples showing you how theSchedule
control could be used. In each sample, you can set the properties with the help of a form and you can add items. These samples use an Access database as the data source. - demo1.aspx shows a sample of a calendar. It also shows you how to delete items with a linkbutton.
- demo2.aspx shows a sample of a task list. It also shows you how to edit and delete items with a linkbutton.
- demo3.aspx shows a sample of a TV program schedule.
- demo4.aspx shows a sample of a calendar with items that span midnight.
How It Works
I decided to use a table for displaying all the items, instead of graphics. Graphics can put a heavy load on the server, and most importantly: a table can have child controls such as checkboxes and hyperlinks. By using templates, a developer has full control about the content of the items.I used Microsoft's sample code for the TemplatedList control as a starting point.
I created a
BaseSchedule
control, which contains the code that is common for ScheduleGeneral
and ScheduleCalendar
.First, all the information is extracted from the data source in order to create lists for the row and column headers. Any double values are removed, and the remainder is sorted in ascending order.
Next, an empty table object is built, with the correct number of rows and columns. For simplicity, the table is always built in vertical layout, and only converted to a horizontal web table later when necessary.
Then, the header items are added (row headers and column headers).
Next, the scheduled items are added to the body of the table. The most difficult part is to calculate the position and span of each item, to merge the corresponding cells, and to check whether items don't overlap.
Finally, the table object is converted into an HTML table.
For the control to work correctly on postback, the control tree needs to be rebuilt in exactly the same way (without setting any properties). To make this possible, some information has to be stored in the control's
ViewState
:- The number of rows
- The number of cells in each row
- The number of header cells in each row
- The position (row and column index) of each item
CreateChildControls
method, this information is used to build the control tree. On postback, viewstate will be applied automatically to each child control, and their state will be restored magically.
I derived from the
CompositeDataBoundControl
class. I added the SelectedValue
property, in order to enable master/detail scenarios with FormView
and DetailsView
controls. For designer support, the controls are derived from the
DataBoundControlDesigner
class. Future
Here are some ideas for improvement:- Add a
DeleteCommand
event - Add a
TodayStyle
property (similar to the one in theCalendar
control) - Provide support for right-to-left languages
- Add dragging support for selecting a range (Outlook style)
- Support for recurring events
- Auto Format option
- Windows Forms version
Points of Interest
- Templated control
- Databound control
- Control styles
- Designer support
- Property builders
- ASP.NET 2.0 databound control
- ASP.NET 2.0 designer action lists
Update History
All releases are fully backwards compatible, except when upgrading from a version prior to 1.5 to version 1.6 or later. For this special case, see the upgrading instructions above. In all other cases, your existing projects will not stop working.Changes in release 2.4.1
- Improved use of the entire column/row in some cases with overlapping cells
Changes in release 2.4
- Simplified algorithm
- Better stacking algorithm
- Now supports multiple repetitions (weeks) in Horizontal mode
Changes in release 2.3
- Removed support for ASP.NET 1.x. From now on, ASP.NET 2.0 or higher is required.
- Extended support for any data source that implements
IEnumerable
orIListSource
Hence, binding toObjectDataSource
orEntityDataSource
for instance is possible. Previous versions only supportDataTable
andDataView
. - Source code is provided as a Visual Studio 2010 project
Changes in release 2.1
- On popular demand, I added the
OnEmptySlotClick
event. To use it, set theEnableEmptySlotClick
property totrue
, and optionally, add a value for theEmptySlotTooltip
property. Handle the event in your page. Seedemo1
anddemo2
for samples. - Added the documentation2.chm help file with documentation for the version for .NET 2.0
Changes in release 2.0
- Template editing from the smart tag menu is now working in VS2005
Changes in release 1.9
- Support for ASP.NET 2.0.
The controls now support declarative binding to ASP.NET 2.0 data source controls. The controls can also serve as master controls in connection with aFormView
orDetailsView
control. As with other ASP.NET 2.0 data controls, this enables you to create pages without writing any code.
There is only one set of source files, with conditional compilation. Use build.bat to build schedule.dll for ASP.NET 1.x and build2.bat to build schedule2.dll for ASP.NET 2.0. Don't use both assemblies in the same project. If you still want to upgrade your pages one by one, make two sets of source files with different namespaces (e.g.rw
andrw2
). - Added the
SelectedValue
property - Added the
SelectedIndexChanged
event
Changes in release 1.6.1
- Added a new property
NumberOfDays
for theScheduleCalendar
control to allow for a number of days other than 7. The default value is still7
. - Since the
ScheduleCalendar
control is not limited to a 7 day week anymore, theWeeks
property is replaced by theNumberOfRepetitions
property. Setting theWeeks
property will still work, but it will not show up in the property panel anymore. - Added a new property
StartDay
for theScheduleCalendar
control to allow for a starting day other thanMonday
. This property is ignored when theNumberOfDays
is not7
. The default value is stillMonday
.
Changes in release 1.6
- A new property
ShowValueMarks
allows for a better display of adjacent items. See the demos and the explanation above. - Added the possibility to add titles to the
ScheduleGeneral
control with no content. This allows you to show all your titles at all times, even when they don't have any scheduled items for the given period. In order to do this, you have to add an extra row in your data source with aDataRangeStartField
value ofNULL
for every title that you want to show up (this solution courtesy of K. B. McHugh). - Added the
EmptyDataTemplate
property. Use this template to provide content when no rows are found in the data source (typically, this would just be a message saying something like "No data found". - Added a new property
ItemStyleField
, which is an optional database field providing the item styles (in the form of a css class name). If not provided, then theItemStyle
property and theAlternatingItemStyle
property will be used for all items. This addition is courtesy of David Sanders. - Added a new property
AutoSortTitles
for theScheduleGeneral
control. Whentrue
(the default value), the titles are sorted alphabetically by the control according to the values in theRangeValueField
(this was also the method used in previous versions). Whenfalse
, the titles are not sorted by the control, and the order will be the one of the items in the datasource. When using this option, make sure that the items with the same titles are grouped together in the datasource before databinding. If the titles are not grouped, unexpected results may occur. - Renamed the database folder in the demos to "App_Data" instead of "db", in preparation for ASP.NET 2.0 folder naming conventions.
Changes in release 1.5
- The code now provides one base class (
BaseSchedule
) and 3 derived controls:ScheduleCalendar
,ScheduleGeneral
andSchedule
(this last one is only for backwards compatibility). This will greatly simplify the setting of their properties and templates, because only the relevant properties and templates are shown for each type of control.
The properties and templates are also more self-explanatory, especially for theScheduleCalendar
control.
Upgrading issues:
Currently, you can leave your existing projects as they are, because the includedSchedule
class provides the same functionality as before, but I plan to remove this class in the future. Therefore, I suggest that you convert your existing projects. This can be done with a few changes only. It will take you only a minute. - The source code is now split up in separate files for each class.
- Added property builders for
ScheduleCalendar
andScheduleGeneral
- Solved
datasource
property bug - Added "
" to empty cells - Databinding errors will now cause a page error (same behavior as in other data controls), instead of silently being suppressed
- Solved bug with databinding in header
- Solved some minor bugs
Changes in release 1.4
- Support for items that span midnight in
Calendar
mode (on popular demand). I added a new property "UseTitleFieldAsDate
", which should be set toFalse
if you want to support items that span midnight. Automatically, these items will be split into multiple items (one for each day), and added to the schedule. I added an extra demo page to show you how to implement this situation. - The timescale can now go up to 24:00 hours (12:00 PM or midnight). I found out that you can enter a 24:00 hours time span in the editor's property pane by using a dot after the number of days ("1.00:00:00" is the same as 24 hours).
- Solved the
DataSource
property bug (showing an error in the property editor). - Added Visual Studio demo files
- Solved bug with postback on header items (reported by Deraldo Messner)
- Added toolbox bitmap
- Important: When upgrading existing projects to this version, you may need to replace the "
rw
" namespace with "schedule.rw
".
Changes in release 1.3
- Support for designer template editing
- Solved some bugs
Changes in release 1.2
- Much better designer support by deriving from
TemplatedControlDesigner
. Almost any change in the properties settings will now automatically be reflected in the designer. - Solved bugs when using
Schedule
with no data in General Mode (reported by Beren Longstreet). - Added a schedule.xsd file for additional IntelliSense support in Visual Studio
Changes in release 1.1
- Made the source code compile with Option Strict On
- Added a
FullTimeScale
option. Previously, only time values occurring in the data source were used. With this option, the control will show a continuous range of time values.