Sample Project
The sample project includes:
C# Source Code
VB.NET Source Code
Visual Studio 2019 Solution
Requirements
.NET Framework 4.0 or higher
Visual Studio 2019 or higher (optional)
Microsoft SQL Server (LocalDB)
Features
This tutorial shows how to create a courtroom schedule web application using DayPilot ASP.NET Scheduler control.
Queue of unscheduled cases
Courtrooms displayed in a tree (with buildings as parents)
Scheduling cases using drag & drop from the list of unscheduled cases
Available time slots limited to working hours (8 am - 6 pm)
Custom CSS theme
License
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.
1. How to create a queue of unscheduled cases
The application displays unscheduled cases in a separate list, next to the Scheduler component.
All cases are stored in the Task
table in the SQL Server database. Cases that have no Assignment
associated to them will be displayed in the queue of unscheduled tasks.
The task list is generated using a simple Repeater
control (AjaxTasks.aspx
).
<asp:Repeater runat="server" id="Repeater1">
<ItemTemplate>
<div class="task" onclick="editTask('<%# DataBinder.Eval(Container.DataItem, "TaskId") %>')" onmousedown="return DayPilotScheduler.dragStart(this, 60*<%# DataBinder.Eval(Container.DataItem, "TaskDuration") %>, '<%# DataBinder.Eval(Container.DataItem, "TaskId") %>', this.innerHTML);">
<span style="cursor: move">:::</span> <%# DataBinder.Eval(Container.DataItem, "TaskName") %> <span><%# DataBinder.Eval(Container.DataItem, "TaskDuration") %>m</span>
</div>
</ItemTemplate>
</asp:Repeater>
The drag & drop functionality is activated by adding an onmousedown
attribute to the list items:
onmousedown="return DayPilotScheduler.dragStart(this, 60*<%# DataBinder.Eval(Container.DataItem, "TaskDuration") %>, '<%# DataBinder.Eval(Container.DataItem, "TaskId") %>', this.innerHTML);"
See also: External Drag and Drop [Scheduler documentation]
The list is is loaded dynamically using a separate AJAX call (jQuery) from the AjaxTasks.aspx
page:
function getTasks() {
$.post('AjaxTasks.aspx', function (result) {
$("#tasks").html(result);
});
}
2. How to display a courtroom schedule
The courtrooms are loaded to the ASP.NET Scheduler control from the Location
database table.
C#
private void LoadResources()
{
DataTable locations = new DataManager().GetLocations();
DayPilotScheduler1.Resources.Clear();
foreach (DataRow location in locations.Rows)
{
int id = Convert.ToInt32(location["LocationId"]);
Resource r = new Resource((string)location["LocationName"], null); // using null for resources that can't be used
r.Expanded = true;
DayPilotScheduler1.Resources.Add(r);
DataTable rooms = new DataManager().GetLocations(id);
foreach (DataRow dr in rooms.Rows)
{
r.Children.Add((string) dr["LocationName"], Convert.ToString(dr["LocationId"]));
}
}
}
VB.NET
Private Sub LoadResources()
Dim locations As DataTable = (New DataManager()).GetLocations()
DayPilotScheduler1.Resources.Clear()
For Each location As DataRow In locations.Rows
Dim id As Integer = Convert.ToInt32(location("LocationId"))
Dim r As New Resource(CStr(location("LocationName")), Nothing) ' using null for resources that can't be used
r.Expanded = True
DayPilotScheduler1.Resources.Add(r)
Dim rooms As DataTable = (New DataManager()).GetLocations(id)
For Each dr As DataRow In rooms.Rows
r.Children.Add(CStr(dr("LocationName")), Convert.ToString(dr("LocationId")))
Next dr
Next location
End Sub
Buildings are loaded first:
C#
DataTable locations = new DataManager().GetLocations();
VB.NET
Dim locations As DataTable = (New DataManager()).GetLocations()
For each building, the list of courtrooms is loaded (using the building id as the parent):
C#
DataTable rooms = new DataManager().GetLocations(id);
VB.NET
Dim rooms As DataTable = (New DataManager()).GetLocations(id)
Creating assignments for buildings (parent tree nodes) is disabled using the PreventParentUsage
property:
PreventParentUsage="false"
The parent nodes are marked with prefix_cellparent
CSS class. This allows to add a visual hint:
.blue_light_header_cellparent {
background-color: #f8f8f8;
}
3. How to create assignments using drag & drop
Unscheduled tasks can be dragged onto the scheduler. The drop event is handled using an EventMove
event handler on the server side:
EventMoveHandling="JavaScript"
EventMoveJavaScript="dp.eventMoveCallBack(e, newStart, newEnd, newResource, {external: external} );"
It is handled using a custom JavaScript handler so we can detect the external drag and drop on the server side (the external
parameter).
C#
protected void DayPilotScheduler1_EventMove(object sender, EventMoveEventArgs e)
{
// check if this row is a courtroom
if (e.NewResource == null)
{
DayPilotScheduler1.DataSource = new DataManager().GetAssignments(DayPilotScheduler1);
DayPilotScheduler1.DataBind();
DayPilotScheduler1.UpdateWithMessage("Sorry, this is not a courtroom. The case cannot be scheduled here.");
return;
}
// check for conflicts with existing assignments
int existing = new DataManager().GetExistingAssignments(e.Value, e.NewStart, e.NewEnd, e.NewResource);
if (existing > 0)
{
DayPilotScheduler1.DataSource = new DataManager().GetAssignments(DayPilotScheduler1);
DayPilotScheduler1.DataBind();
DayPilotScheduler1.UpdateWithMessage(String.Format("The case cannot be scheduled here. It conflicts with {0} other assignments.", existing));
return;
}
// check the business hours (8 - 18)
if (e.NewStart.Hour < 8 || e.NewEnd > new DateTime(e.NewEnd.Year, e.NewEnd.Month, e.NewEnd.Day, 18, 0, 0))
{
DayPilotScheduler1.DataSource = new DataManager().GetAssignments(DayPilotScheduler1);
DayPilotScheduler1.DataBind();
DayPilotScheduler1.UpdateWithMessage("Sorry, it can't be scheduled outside of the business hours.");
return;
}
// scheduling a new case
if ((bool)e.Data["external"])
{
new DataManager().CreateAssignment(e.NewStart, e.NewEnd, Convert.ToInt32(e.NewResource), Convert.ToInt32(e.Value));
DayPilotScheduler1.UpdateWithMessage("The assignment has been created.");
}
// moving an existing assignment
else
{
new DataManager().MoveAssignment(Convert.ToInt32(e.Value), e.NewStart, e.NewEnd, e.NewResource);
DayPilotScheduler1.UpdateWithMessage("The assignment has been updated.");
}
DayPilotScheduler1.DataSource = new DataManager().GetAssignments(DayPilotScheduler1);
DayPilotScheduler1.DataBind();
}
VB.NET
Protected Sub DayPilotScheduler1_EventMove(ByVal sender As Object, ByVal e As EventMoveEventArgs)
' check if this row is a courtroom
If e.NewResource Is Nothing Then
DayPilotScheduler1.DataSource = (New DataManager()).GetAssignments(DayPilotScheduler1)
DayPilotScheduler1.DataBind()
DayPilotScheduler1.UpdateWithMessage("Sorry, this is not a courtroom. The case cannot be scheduled here.")
Return
End If
' check for conflicts with existing assignments
Dim existing As Integer = (New DataManager()).GetExistingAssignments(e.Value, e.NewStart, e.NewEnd, e.NewResource)
If existing > 0 Then
DayPilotScheduler1.DataSource = (New DataManager()).GetAssignments(DayPilotScheduler1)
DayPilotScheduler1.DataBind()
DayPilotScheduler1.UpdateWithMessage(String.Format("The case cannot be scheduled here. It conflicts with {0} other assignments.", existing))
Return
End If
' check the business hours (8 - 18)
If e.NewStart.Hour < 8 OrElse e.NewEnd > New Date(e.NewEnd.Year, e.NewEnd.Month, e.NewEnd.Day, 18, 0, 0) Then
DayPilotScheduler1.DataSource = (New DataManager()).GetAssignments(DayPilotScheduler1)
DayPilotScheduler1.DataBind()
DayPilotScheduler1.UpdateWithMessage("Sorry, it can't be scheduled outside of the business hours.")
Return
End If
' scheduling a new case
If CBool(e.Data("external")) Then
CType(New DataManager(), DataManager).CreateAssignment(e.NewStart, e.NewEnd, Convert.ToInt32(e.NewResource), Convert.ToInt32(e.Value))
DayPilotScheduler1.UpdateWithMessage("The assignment has been created.")
' moving an existing assignment
Else
CType(New DataManager(), DataManager).MoveAssignment(Convert.ToInt32(e.Value), e.NewStart, e.NewEnd, e.NewResource)
DayPilotScheduler1.UpdateWithMessage("The assignment has been updated.")
End If
DayPilotScheduler1.DataSource = (New DataManager()).GetAssignments(DayPilotScheduler1)
DayPilotScheduler1.DataBind()
End Sub
The EventMove
event handler performs a few checks before actually saving the changes:
Check if the resource (row) is a room, and not a building.
Check if there is no other case scheduled for the same room and time.
Check if the case is scheduled within the working hours.
In the case of external drag & drop (from the list of unscheduled tasks) it creates a new record in the Assignment
table.
Otherwise, it updates the existing Assignment
.
4. How to define working hours
By default, the ASP.NET Scheduler displays a full 24-hour range for each day.
We can limit it to the actual working hours using the ShowNonBusiness
property:
BusinessBeginsHour="8"
BusinessEndsHour="18"
ShowNonBusiness="false"
See also: Hiding Non-Business Hours
5. How to style the ASP.NET Scheduler using CSS rheme
The CSS theme was created using the online CSS Theme Designer. It is saved at the following URL:
You can apply a theme using the Theme
property:
Theme="blue_light_header"
6. SQL Server database schema
This is the schema (DDL) of the attached SQL Server database (daypilot.mdf
):
Assignment
table:
CREATE TABLE [dbo].[Assignment] (
[AssignmentId] BIGINT IDENTITY (1, 1) NOT NULL,
[LocationId] BIGINT NOT NULL,
[AssignmentNote] VARCHAR (2000) NULL,
[AssignmentStart] DATETIME NOT NULL,
[AssignmentEnd] DATETIME NOT NULL,
[TaskId] BIGINT NULL,
CONSTRAINT [PK_Assignment] PRIMARY KEY CLUSTERED ([AssignmentId] ASC)
);
Location
table:
CREATE TABLE [dbo].[Location] (
[LocationId] BIGINT IDENTITY (1, 1) NOT NULL,
[LocationName] VARCHAR (200) NOT NULL,
[ParentId] BIGINT NULL,
CONSTRAINT [PK_Location] PRIMARY KEY CLUSTERED ([LocationId] ASC)
);
Task
table:
CREATE TABLE [dbo].[Task] (
[TaskId] BIGINT IDENTITY (1, 1) NOT NULL,
[TaskDuration] INT NULL,
[TaskName] VARCHAR (200) NULL,
CONSTRAINT [PK_Task] PRIMARY KEY CLUSTERED ([TaskId] ASC)
);