Sample Project

The sample project includes:


  • .NET Framework 4.0 or higher

  • Visual Studio 2019

  • Microsoft SQL Server 2014+ (Express) 


This tutorial shows how to create a timetable in an ASP.NET web application. It supports custom time slots (blocks), drag and drop event moving and resizing, and custom event colors.

  • Weekly timetable view

  • Displays time slots (blocks) with custom size (the block number is mapped to the hour component of a DateTime value)

  • Inline editing of the block properties using active areas

  • Custom event color in combination with CSS styling

  • Loads events and blocks from SQL Server database.

  • Full calendar CSS styling

  • Integrates DayPilot Navigator for switching the week

  • C# and VB.NET source code included.

  • Sample database included.


Licensed for testing and evaluation purposes. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for ASP.NET WebForms.


This tutorial doesn't cover the DayPilot ASP.NET Calendar basics. For more information about  configuring the week view, loading events from a database or enabling the AJAX drag and drop operations (moving, resizing) please see the following tutorial:

1. Mapping Timetable Slots to Hours

Internally, the ASP.NET calendar control works with time slots of equal size. The maximum supported slot size is 60 minutes (CellDuration property).

In order to show a timetable with custom blocks, it is necessary to map the block to the hour part of the DateTime. This allows a maximum of 24 slots per day. It would be possible to map it to the minute part as well if needed (modify TimetableManager.cs/TimetableManager.vb class).

Day (June 1, 2021)

  • Block 1 => mapped as 2021-06-21T01:00:00

  • Block 2 => mapped as 2021-06-21T02:00:00

  • etc.

Day 2 (June 22, 2021)

  • Block 1 => mapped as 2021-06-22T01:00:00

  • Block 2 => mapped as 2021-06-22T02:00:00

  • etc.

We will limit the display to 7 time slots (using DayBeginsHour, DayEndsHour, HeightSpec properties), each taking one hour (CellDuration property). We will increase the default time cell size to 70 pixels (CellHeight property):

    /> timetable default week view

Now we will use BeforeTimeHeaderRender to customize the time headers (vertical axis). We will replace the default text with the block name (start and end) as defined in the database. The real block start and end times are defined in the [Block] database table:

timetable sql block names

The block start and end times (BlockStart, BlockEnd) are stored as DateTime fields but only the time part is significant.

Our BeforeTimeHeaderRender handler looks like this:


protected void DayPilotCalendar1_OnBeforeTimeHeaderRender(BeforeTimeHeaderRenderEventArgs ea)
    int id = ea.Start.Hours;
    DataRow r = FindBlock(id);
    ea.InnerHTML = String.Format("Block {0}<br/>{1}<br/>{2}", id, TimeFormatter.GetHourMinutes((DateTime)r["BlockStart"], DayPilotCalendar1.TimeFormat), TimeFormatter.GetHourMinutes((DateTime)r["BlockEnd"], DayPilotCalendar1.TimeFormat));


Protected Sub DayPilotCalendar1_OnBeforeTimeHeaderRender(ByVal ea As BeforeTimeHeaderRenderEventArgs)
	Dim id As Integer = ea.Start.Hours
	Dim r As DataRow = FindBlock(id)
	ea.InnerHTML = String.Format("Block {0}<br/>{1}<br/>{2}", id, TimeFormatter.GetHourMinutes(CDate(r("BlockStart")), DayPilotCalendar1.TimeFormat), TimeFormatter.GetHourMinutes(CDate(r("BlockEnd")), DayPilotCalendar1.TimeFormat))
End Sub

It will replace the default header text (e.g. "1 AM") with the block details: timetable time slot names

2. Timetable Block Editing

We will make the block properties (start and end time) editable by adding an active area to each time header cell: timetable active area block edit

The active area is added in BeforeTimeHeaderRender event handler:


protected void DayPilotCalendar1_OnBeforeTimeHeaderRender(BeforeTimeHeaderRenderEventArgs ea)
	// ...

	ea.Areas.Add(new Area().Width(15).Top(0).Bottom(0).Right(0).CssClass("resource_action_menu").Html("<div><div></div></div>").JavaScript("editBlock(e);"));


Protected Sub DayPilotCalendar1_OnBeforeTimeHeaderRender(ByVal ea As BeforeTimeHeaderRenderEventArgs)
	Rem ...
	ea.Areas.Add((New Area()).Width(15).Top(0).Bottom(0).Right(0).CssClass("resource_action_menu").Html("<div><div></div></div>").JavaScript("editBlock(e);"))
End Sub

It specifies the area dimensions, CSS class and associated action. This active area will execute editBlock() JavaScript method when clicked. The editBlock() methods opens a dialog box with block details: timetable block edit modal dialog

The modal dialog is created using DayPilot.Modal.form() method from DayPilot Modal open-source library. This method lets you create a modal dialog with a dynamically-built form. You can design your own modal dialog using DayPilot Modal Builder application.

3. Custom Timetable Event Colors

The new event dialog allows users to add events to the timetable and specify event properties, including description and color. timetable new event dialog

Blocks are loaded from the database:



private void FillDropDownListStart()
	DataTable blocks = new DataManager().GetBlocks();

	foreach(DataRow r in blocks.Rows)
		int id = Convert.ToInt32(r["BlockId"]);
		DateTime start = (DateTime) r["BlockStart"];
		DateTime end = (DateTime)r["BlockEnd"];
		string name = String.Format("Block {0} ({1} - {2})", id, TimeFormatter.GetHourMinutes(start, TimeFormat.Auto), TimeFormatter.GetHourMinutes(end, TimeFormat.Auto));
		ListItem item = new ListItem(name, id.ToString());


Private Sub FillDropDownListStart()
	Dim blocks As DataTable = (New DataManager()).GetBlocks()

	For Each r As DataRow In blocks.Rows
		Dim id As Integer = Convert.ToInt32(r("BlockId"))
		Dim start As Date = CDate(r("BlockStart"))
		Dim [end] As Date = CDate(r("BlockEnd"))
		Dim name As String = String.Format("Block {0} ({1} - {2})", id, TimeFormatter.GetHourMinutes(start, TimeFormat.Auto), TimeFormatter.GetHourMinutes([end], TimeFormat.Auto))
		Dim item As New ListItem(name, id.ToString())
	Next r
End Sub



public DataTable GetBlocks()
	var da = CreateDataAdapter("select * from [Block] order by [BlockId]");
	DataTable dt = new DataTable();
	return dt;


Public Function GetBlocks() As DataTable
	Dim da = CreateDataAdapter("select * from [Block] order by [BlockId]")
	Dim dt As New DataTable()
	Return dt
End Function

The event details are saved in the [Assignment] table:

timetable sql schema assignment

The event color field (AssignmentColor) is used to store the event color. This field can't be mapped to the event color directly but we can do it in BeforeEventRender event handler:


protected void DayPilotCalendar1_BeforeEventRender(object sender, DayPilot.Web.Ui.Events.Calendar.BeforeEventRenderEventArgs e)
	string color = (string) e.DataItem["AssignmentColor"];
	if (!String.IsNullOrEmpty(color))
		e.BackgroundColor = color;
		e.BorderColor = color;
		e.FontColor = "#ffffff";


Protected Sub DayPilotCalendar1_BeforeEventRender(ByVal sender As Object, ByVal e As DayPilot.Web.Ui.Events.Calendar.BeforeEventRenderEventArgs)
	Dim color As String = CStr(e.DataItem("AssignmentColor"))
	If Not String.IsNullOrEmpty(color) Then
		e.BackgroundColor = color
		e.BorderColor = color
		e.FontColor = "#ffffff"
	End If
End Sub

Timetable SQL Server Database

The timetable tutorial project includes an SQL Server database. The database file (daypilot.mdf) can be found in the App_Data directory.

The timetable application connects to the SQL Server database using a LocalDB connector:


<?xml version="1.0"?>
    <add name="daypilot" connectionString="data source=(LocalDB)\MSSQLLocalDB;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\daypilot.mdf" providerName="System.Data.SqlClient"/>



Database Schema (DDL)

CREATE TABLE [dbo].[Assignment] (
    [AssignmentId]    BIGINT         IDENTITY (1, 1) NOT NULL,
    [AssignmentNote]  VARCHAR (2000) NULL,
    [AssignmentStart] DATETIME       NOT NULL,
    [AssignmentEnd]   DATETIME       NOT NULL,
    [AssignmentColor] VARCHAR (50)   NULL,

CREATE TABLE [dbo].[Block] (
    [BlockId]    INT      NOT NULL,
    [BlockStart] DATETIME NOT NULL,
    [BlockEnd]   DATETIME NOT NULL,