Sample Project

The sample project includes:

Online Demo

Requirements

Visual Studio 2010 Solution

  • .NET Framework 4.0 or higher
  • Visual Studio 2010 or higher (optional)
  • Microsoft SQL Server (Express) 

Visual Studio 2012 Solution

  • .NET Framework 4.0 or higher
  • Visual Studio 2012 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 unscheduled list
  • 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. Buy a license.

1. Queue of Unscheduled Cases

courtroom-schedule-case-queue.png

There is a separate list of unscheduled cases. All cases are represented using [Task] table in the database. Cases that have no [Assignment] associated to them will be displayed in the queue on the left side.

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 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 AjaxTasks.aspx page:

function getTasks() {
  $.post('AjaxTasks.aspx', function (result) {
    $("#tasks").html(result);
  });
}

2. Displaying the courtroom schedule

courtroom-schedule-locations.png

The courtrooms are loaded to the Scheduler control from [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 PreventParentUsage property:

PreventParentUsage="false"

The parent nodes are marked using prefix_cellparent CSS class. This allows to add a visual hint:

.blue_light_header_cellparent {
	background-color: #f8f8f8;
}

3. Creating Assignments using Drag&Drop

courtroom-schedule-drag-and-drop.png

Unscheduled tasks can be dragged onto the scheduler. The drop event is handled using 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 ("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 is making a few checks before actually saving the changes:

  1. Check if the resource (row) is a room, and not a building.
  2. Check if there is no other case scheduled for the same room and time.
  3. 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. Working Hours

courtroom-schedule-working-hours.png

By default, the Scheduler displays full 24 hour range for each day.

We can limit it to the actual working hours using ShowNonBusiness property:

BusinessBeginsHour="8"
BusinessEndsHour="18"
ShowNonBusiness="false"

See also: Hiding Non-Business Hours

5. CSS Theme

scheduler-css-theme-admsiu.png

The CSS theme was created using the online CSS Theme Designer.

It is saved at the following URL:

It is applied using the following properties:

CssOnly="true"
CssClassPrefix="blue_light_header"