Download Free Demos

Loading Data

Basic Principles

The client-side dhtmlxScheduler that lies in the core of dhtmlxScheduler.NET accepts data in a plain JSON format which is either loaded via ajax or put into the scheduler from object data source.

While these methods can be used as they are, Scheduler.NET provides wrappers that allow implementing CRUD without worrying about the format of the loaded data.

Let's start with the basic conventions you'll see in the most of our samples.

ASP.NET MVC

The code of the Controller for ASP.NET MVC may look as follows:

public class CalendarController : Controller
{
    public ActionResult Index()
    {
        var scheduler = new DHXScheduler(this);
 
        // ajax call for Data on loading
        scheduler.LoadData = true;
 
        // send changes to the Save action
        scheduler.EnableDataprocessor = true;
 
        return View(scheduler);
    }
 
    public ContentResult Data()
    {
        var data = new SchedulerAjaxData(
                new List<CalendarEvent>{
                    new CalendarEvent{
                        id = 1,
                        text = "Sample Event",
                        start_date = new DateTime(2017, 09, 03, 6, 00, 00),
                        end_date = new DateTime(2017, 09, 03, 8, 00, 00)
                    },
                    new CalendarEvent{
                        id = 2,
                        text = "New Event",
                        start_date = new DateTime(2017, 09, 05, 9, 00, 00),
                        end_date = new DateTime(2017, 09, 05, 12, 00, 00)
                    },
                    new CalendarEvent{
                        id = 3,
                        text = "Multi-day Event",
                        start_date = new DateTime(2017, 09, 03, 10, 00, 00),
                        end_date = new DateTime(2017, 09, 10, 12, 00, 00)
                    }
                }
            );
        return (ContentResult)data;
    }
 
    public ContentResult Save(int? id, FormCollection actionValues)
    {
        var action = new DataAction(actionValues);
 
        try
        {
            var changedEvent = DHXEventsHelper.Bind<CalendarEvent>(actionValues);
 
            switch (action.Type)
            {
                case DataActionTypes.Insert:
                    //do insert
                    //action.TargetId = changedEvent.id; // assign post operational id
                    break;
                case DataActionTypes.Delete:
                    //do delete
                    break;
                default:// "update"                          
                    //do update
                    break;
            }
        }
        catch
        {
            action.Type = DataActionTypes.Error;
        }
        return (ContentResult)new AjaxSaveResponse(action);
    }
}

Let's consider Index and Data actions. The Save action is explained here.

Specifying the data source

As you can see, inside the Index action we create scheduler and tell it to load data:

var scheduler = new DHXScheduler(this);
scheduler.LoadData = true;

And the Data action serves as a data source.

What do we need to know now:

  • If you create scheduler instance using DHXScheduler(Controller parent) constructor, scheduler will use the action of the controller named "Data" as a data source, so you don't have to specify paths manually.
  • Thus, if you use scheduler in ASP.NET MVC, usually you don't need to specify the URL manually, just define the Data action.
  • Alternatively, you can specify a different action or an URL using the DHXScheduler.DataAction property. See the examples below.

Specifying a different action:

var scheduler = new DHXScheduler(this);
scheduler.DataAction = "Events";
scheduler.LoadData = true;

Specifying a complete URL explicitly:

var scheduler = new DHXScheduler(); // !! use default constructor
scheduler.DataAction = Url.Content("~/data.ashx");
scheduler.LoadData = true;

Implementing the data source

At the code level scheduler accepts JSON data which can be generated quite easily from objects.

However, due to special requirements to the naming of model properties and compatible Date-Time formats, we encourage you to use the DHTMLX.Scheduler.Data.SchedulerAjaxData class which provides JSON serializer for IEnumerable collections. The examples of usage are given below.

ASP.NET MVC

public ContentResult Data()
{
    var data = new SchedulerAjaxData(
            new List<CalendarEvent>{
                new CalendarEvent{
                    id = 1,
                    text = "Sample Event",
                    start_date = new DateTime(2012, 09, 03, 6, 00, 00),
                    end_date = new DateTime(2012, 09, 03, 8, 00, 00)
                },
                new CalendarEvent{
                    id = 2,
                    text = "New Event",
                    start_date = new DateTime(2012, 09, 05, 9, 00, 00),
                    end_date = new DateTime(2012, 09, 05, 12, 00, 00)
                },
                new CalendarEvent{
                    id = 3,
                    text = "Multi-day Event",
                    start_date = new DateTime(2012, 09, 03, 10, 00, 00),
                    end_date = new DateTime(2012, 09, 10, 12, 00, 00)
                }
            }
        );
    return (ContentResult)data;
}

ASP.NET WebForms

<%@ WebHandler Language="C#" CodeBehind="Data.ashx.cs" Class="SchedulerNetAsp.Data" %>
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
 
using DHTMLX.Scheduler.Data;
 
namespace SchedulerNetAsp
{
    public class Data : IHttpHandler
    {
 
        public void ProcessRequest(HttpContext context)
        {
            var data = new SchedulerAjaxData(
                new List<CalendarEvent>{
                    new CalendarEvent{
                        id = 1,
                        text = "Sample Event",
                        start_date = new DateTime(2012, 09, 03, 6, 00, 00),
                        end_date = new DateTime(2012, 09, 03, 8, 00, 00)
                    },
                    new CalendarEvent{
                        id = 2,
                        text = "New Event",
                        start_date = new DateTime(2012, 09, 05, 9, 00, 00),
                        end_date = new DateTime(2012, 09, 05, 12, 00, 00)
                    },
                    new CalendarEvent{
                        id = 3,
                        text = "Multi-day Event",
                        start_date = new DateTime(2012, 09, 03, 10, 00, 00),
                        end_date = new DateTime(2012, 09, 10, 12, 00, 00)
                    }
                }
            );
 
            context.Response.ContentType = "text/json";
            context.Response.Write(data.ToString());
        }
 
        public bool IsReusable { get { return false; } }
    }
}

Data to load

Mandatory properties

The SchedulerAjaxData helper accepts IEnumerable of generic objects as data collection, which means there are no predefined event classes you can use. Instead, you can provide any custom or anonymous classes and ensure they provide the following public properties:

  • id (int, string, etc.) - the event id
  • start_date (DateTime or string) - the date when a task is scheduled to begin. The default format is "%m/%d/%Y %H:%i"
  • end_date (DateTime or string) - the date when a task is scheduled to be completed. The default format is "%m/%d/%Y %H:%i"
  • text (string) - the text of a task

In addition to these properties, items can contain any number of custom properties. Once loaded into calendar, all of them will be available to the client-side API.

Note that items are converted to JSON, thus they shouldn't contain any circular references. SchedulerAjaxData also provides means for skipping circular references from JSON, please see the details below.

C# class definition

public class CalendarEvent
{
    // id, text, start_date and end_date properties are mandatory
    public int id { get; set; }
    public string text { get; set; }
    public DateTime start_date { get; set; }
    public DateTime end_date { get; set; }
}

T-SQL table declaration

 
CREATE TABLE [CalendarEvents] (
  [id]           INT           IDENTITY (1, 1) NOT NULL,
  [text]         TEXT          NULL,
  [start_date]   DATETIME      NOT NULL,
  [end_date]     DATETIME      NOT NULL,
  PRIMARY KEY (id)
);

Recurring events

The possibility to use Recurring Events requires extending data objects with additional properties. If you use recurring events, a minimal set of public properties will be the following

  • id - (int, string, etc.) the event id
  • start_date - (DateTime or string) the date when a task is scheduled to begin. The default format is "%m/%d/%Y %H:%i"
  • end_date - (DateTime or string) the date when a task is scheduled to be completed. The default format is "%m/%d/%Y %H:%i"
  • text - (string ) the text of a task
  • event_length - (long int, string) duration of recurring series event in seconds, or the timestamp of modified instance. Can be empty or null.
  • event_pid - (int, string) link to the id of parent series. Can be empty or null.
  • rec_type - (string) the recurrence pattern of series (e.g. "repeat every Tuesday"). Can be empty.

Again, any custom properties can be used.

C# class definition

public class RecurringEvent
{
    public int id { get; set; }
    public string text { get; set; }
    public DateTime start_date { get; set; }
    public DateTime end_date { get; set; }
    public long? event_length { get; set; }
    public int? event_pid { get; set; }
    public string rec_type { get; set; }
}

T-SQL table declaration

CREATE TABLE [Events] (
  [id]           INT           IDENTITY (1, 1) NOT NULL,
  [text]         TEXT          NULL,
  [start_date]   DATETIME      NOT NULL,
  [end_date]     DATETIME      NOT NULL,
  [event_length] BIGINT        NULL,
  [rec_type]     NVARCHAR(50)  NULL,
  [event_pid]    INT           NULL,
  PRIMARY KEY (id)
);

Big datasets

By default, Scheduler loads all data at once. It may become problematic when you use big event collections. In such situations you can use dynamic loading and load data by parts necessary to fill the viewable area of Scheduler.

To enable dynamic loading, you need to call the EnableDynamicLoading() method:

public ActionResult Index(){
  ...
  var scheduler = new DHXScheduler(this);
  scheduler.EnableDynamicLoading(SchedulerDataLoader.DynamicalLoadingMode.Day);
  ...
}

The enumeration SchedulerDataLoader.DynamicalLoadingMode has 4 possible values:

  • year
  • month
  • week
  • day

For example, if you set the 'month' mode, Scheduler will request data just for the current month and load remaining ones on demand.

Generated requests look like this:

Data?from=DATEHERE&to=DATEHERE

where DATEHERE is a valid date value of the yyyy-MM-dd format.

The received data can be parsed as follows:

var dateFrom = DateTime.ParseExact(this.Request.QueryString["from"], "yyyy-MM-dd", CultureInfo.InvariantCulture);
var dateTo = DateTime.ParseExact(this.Request.QueryString["to"], "yyyy-MM-dd", CultureInfo.InvariantCulture);

Static Loading

Static loading means that scheduler will be populated with the data during initialization and ajax loading won't be needed.

public ActionResult Index() {
   var scheduler = new DHXScheduler(this);
   var context = new DHXSchedulerDataContext();
   scheduler.Data.Parse(context.Events.ToList()));
   scheduler.EnableDataprocessor = true;
   return View(scheduler);
}

Added items are stored in the inner object of the DHXSchedulerDataStore class called scheduler.Data.Pull.

You can get a certain item by its index using the line below:

var item = scheduler.Data.Pull[0];

Custom names for data properties

To use a custom name for a mandatory data property, use the [DHXJson(Alias = 'mandatory_name')] serialization attribute.

For example, you have a class with the following properties:

using DHTMLX.Scheduler;
public class DataItem
{
    public int EventId { get; set; }
    public string Description { get; set; }
    public DateTime Start { get; set; }
    public DateTime End { get; set; }
}

To load data as it is without changing the names of members (i.e. EventId → id, Description → text), alter the code as in:

using DHTMLX.Scheduler;
public class DataItem
{
    [DHXJson(Alias = "id")]
    public int EventId { get; set; }
    [DHXJson(Alias = "text")]
    public string Description { get; set; }
    [DHXJson(Alias = "start_date")]
    public DateTime Start { get; set; }
    [DHXJson(Alias = "end_date")]
    public DateTime End { get; set; }
}

Preventing data properties from sending to the client

While sending data properties to the client, you can exclude some of them from the list, if needed. To do this, use the [DHXJson(Ignore = 'true')] serialization attribute as in:

// the 'RelatedEvents' property won't be sent to the client
public class DataItem
{
    public int id { get; set; }
    public string text { get; set; }
    public DateTime start_date { get; set; }
    public DateTime end_date { get; set; }
    [DHXJson(Ignore=true)]
    public List<CalendarEvent> RelatedEvents { get; set; }
}

Note, you can't 'ignore' the mandatory properties: 'id', 'text', 'start_date', 'end_date'.

Custom render function

By default, the built-in serializer renders all public data properties. If you want to render a part of properties or your data objects don't fit the stated data requirements, you need to specify a custom render function.

How can you set a custom render function for data?

In most code samples we use the following technique for loading data:

public ContentResult Data()
{
    var data = new SchedulerAjaxData((new DHXSchedulerDataContext()).Events);
    return data;
}

But in reality, the code above is just a short variant of the following one:

public ContentResult Data()
{
    var data = new SchedulerAjaxData((new DHXSchedulerDataContext()).Events);
    return Content( data.Render() );
}

The SchedulerAjaxData.Render() method has 2 overloads:

1 . public string Render()
2 . public string Render(Action <System.Text.StringBuilder, object> renderer)

where:

  • renderer - a custom function that renders event objects.

The first overload is used during standard data loading. The second one allows specifying a custom render function (see a usage example below).

public ContentResult Data()
{
    var data = new SchedulerAjaxData((new DHXSchedulerDataContext()).Events);
    return Content( data.Render(eventRenderer) );
}
 
public void eventRenderer(System.Text.StringBuilder builder, object ev)
{
    var item = ev as Event;
    builder.Append(
        string.Format(":{0}, text:\"{1}\", start_date:\"{2:MM/dd/yyyy HH:mm}\", end_date:\"{3:MM/dd/yyyy HH:mm}\"",
        item.id,
        HttpUtility.JavaScriptStringEncode(item.text),
        item.start_date,
        item.end_date)
    );
}

While defining a custom function, please remember that you need to escape characters that may break JSON (such as quotes, newlines, etc.). For this purpose you can use the HttpUtility.JavaScriptStringEncode utility (.Net 4.0+), as it's shown in the above code. If you use an older version of .NET, you should provide some custom solution.

Custom Date Format

If you set a custom value for the DHXScheduler.Config.xml_date configuration option in the Index() action, don't forget to set the same date format for the SchedulerAjaxData object.

public ActionResult Index(){
    ...
    scheduler.Config.xml_date = "%d/%m/%Y %H:%i";
    ...
}
 
public ContentResult Data(){
    var events = (new DHXSchedulerDataContext()).Events;
    var data = new SchedulerAjaxData(events);
 
    data.DateFormat = "%d/%m/%Y %H:%i";
    return (data);
}

See available variations of the format string here.

Updating Data from Server Side

When you need to update data depending on the result of a server-side operation, you should do 2 things:

1 .Set the flag sched.Data.DataProcessor. UpdateFieldsAfterSave to true (in the Index() action).

2 .Set new values for properties in the response through the UpdateField(string fieldName, object fieldValue) method of the AjaxSaveResponse class (in the Save() action).

For example, you want to show events in different colors: events that are previous to the current date colored in gray, others - in blue.

So, you set the flag:

public ActionResult Index() {
  var scheduler = new DHXScheduler(this);
   ...
  scheduler.UpdateFieldsAfterSave();
   ...
  return View(scheduler);
}

and assign the needed color:

public ContentResult Save(ColoredEvent changedEvent, FormCollection actionValues) {
  var action = new DataAction(actionValues);
  var color = "";
  if (changedEvent.start_date < DateTime.Now)
         color = "gray";
  else
         color = "blue";
  ...
 
  var result = new AjaxSaveResponse(action);
  result.UpdateField("textColor", color); // adds a new value
  return result;
}

Loading Custom Data

You can provide SchedulerAjaxData response with additional data that can later be accessed on the client side. Additional items are added as named collections to the SchedulerAjaxData.ServerList dictionary:

var data = new SchedulerAjaxData();
data.Add(new List<object>{
    new { id = 1, text = "Event 1", start_date = new DateTime(2014, 12, 4, 10, 0, 0), end_date = new DateTime(2014, 12, 4, 12, 0, 0) },
    new { id = 2, text = "Event 2", start_date = new DateTime(2014, 12, 4, 13, 15, 0), end_date = new DateTime(2014, 12, 4, 14, 0, 0) },
    new { id = 3, text = "Event 3", start_date = new DateTime(2014, 12, 4, 13, 0, 0), end_date = new DateTime(2014, 12, 4, 13, 30, 0) },
});
 
data.ServerList.Add("dayoff", new List<object>{
    new { Date = new DateTime(2014, 11, 1)},
    new { Date = new DateTime(2014, 12, 14)}
});
 
return (ContentResult)data;

When such response is loaded to the page, the client-side component will fire the onOptionsLoad event, followed by the onXLE event.

The collection can be accessed on the client side using scheduler.serverList method (JavaScript):

var dayoff = scheduler.serverList("dayoff");

Note, for correct work, server list has to be initialized on the client side before loading. In order to do so, call scheduler.serverList("listName") before initialization of scheduler:

// C#
var scheduler = new DHXScheduler();
scheduler.BeforeInit.Add("initServerLists();");
// JS
function initServerLists(){
    scheduler.serverList("dayoff");
}

Loading Sections of Timeline and Units

Scheduler supports loading Timeline and Units options by default. It can be done by setting the ServerList property of Timeline or Units instance.

Initialization:

var scheduler = new DHXScheduler(this);
var timeline = new TimelineView("timeline", "section_id") {
    RenderMode = TimelineView.RenderModes.Bar,
    X_Unit = TimelineView.XScaleUnits.Day,
    X_Size = 7,
    X_Date = "%d"
};
scheduler.Views.Add(timeline);
timeline.ServerList = "sections";

Loading options:

var data = new SchedulerAjaxData(events);
 
data.ServerList.Add("sections", new List<object>{
     new { key = "1", label = "Section 1" },
     new { key = "2", label = "Section 2" },
     new { key = "1", label = "Section 3" }
});

In this case no additional code is required. Timeline and Units views will fetch options from the lists with the specified names.

Handling Errors

In the case of network errors or invalid server response, the client side will fire the "onLoadError" event, which can be captured with a JavaScript code:

scheduler.attachEvent("onLoadError", function(resp){
    dhtmlx.message("The service is currently offline...");
});

See the event details here.

Common Issues and Use Cases

Disable cache and force refresh when changing dates

Use case: You need to refresh/reload calendar events each time user changes date in calendar.

As as solution, firstly you need to enable dynamic loading to load displayed date ranges on demand.

However, the built-in dynamic loading caches previously loaded values, so once you preload events for a certain date range, scheduler won't reload them from the server again.

It can be worked around by manually clearing scheduler on date change, by using client-side API.

// JS
scheduler.attachEvent("onBeforeViewChange", function (oldMode, oldDate, mode, date) {
    if (oldMode != mode || +oldDate != +date)
        scheduler.clearAll();
    return true;
});

Reload Timeline/Units sections on date change

In case sections of the Timeline/Units view depend on the displayed date, they can be reloaded dynamically.

  • Enable dynamic loading as shown here
  • Disable data loading cache as shown in the previous section, in order to ensure new data will be requested each time the user changes date:
scheduler.attachEvent("onBeforeViewChange", function (oldMode, oldDate, mode, date) {
    if (oldMode != mode || +oldDate != +date)
        scheduler.clearAll();
    return true;
});
  • Load sections from the Data action as shown here in order to reload sections each time when data is requested.

Scheduler initialization

// C#
public ActionResult Index()
{
    var scheduler = new DHXScheduler(this);
 
    scheduler.Extensions.Add("../scheduler-config.js");// will define js settings here
 
    var timeline = new TimelineView("timeline", "room_id");
    scheduler.Views.Add(timeline);
    timeline.ServerList = "timeline";
 
    scheduler.EnableDynamicLoading(SchedulerDataLoader.DynamicalLoadingMode.Day);
    scheduler.LoadData = true;
    scheduler.EnableDataprocessor = true;
 
    return View(scheduler);
}

Loading data

// C#
public ContentResult Data(DateTime from, DateTime To)
{
    var data = new SchedulerAjaxData();
 
    data.Add(this.db
        .Events
        .Where(e => e.start_date < from && e.end_date > to)
        .ToList()
    );
 
    // select sections by required condition and add them to ServerList 
    // using the same name specified in Timeline configuration
    data.ServerList.Add("timeline", new List<object>{
        new {key = 1, label = "row 1"},
        new {key = 2, label = "row 2"},
        new {key = 3, label = "row 3"}
    });
 
    return (ContentResult)data;
}

Scripts/scheduler-config.js

// JS
scheduler.attachEvent("onBeforeViewChange", function (oldMode, oldDate, mode, date) {
    if (oldMode != mode || +oldDate != +date)
        scheduler.clearAll();
    return true;
});

System.StackOverflowException during data loading

Such issue may be caused by Foreign Keys and cross-references between models that create circular structure which can't be serialized to JSON. For example, consider the following EF models:

public class Event
{
    public int id{get;set;}
    public string text{get;set;}
    public DateTime start_date{get;set;}
    public DateTime end_date {get;set;}
 
    public int WorkplaceId {get;set;}
    public Workplace Workplace {get;set;}
}
 
public class Workplace
{
    public int Id {get; set;};
    public string Name {get; set;}
 
    public ICollection<Event> Events {get; set;}
}

Which are connected via the WorkplaceId foreign key. Such model can't be serialized as is due to circular references Event.Workplace <-> Workplace.Events. Thus, object properties has to be excluded from the dataset. For this, you can choose one of the two ways below:

  • Remove unsafe properties using DHXJsonAttribute:
using DHTMLX.Scheduler;
public class Event
{
    public int id{get;set;}
    public string text{get;set;}
    public DateTime start_date{get;set;}
    public DateTime end_date {get;set;}
 
    public int WorkplaceId {get;set;}
 
    [DHXJson(Ignore=true)]
    public Workplace Workplace {get;set;}
}
  • Alternatively, select properties you need in Data handler explicitly
public ContentResult Data()
{
    var data = new SchedulerAjaxData(
           this.db // this.db - instance of database context
           .Events
           .Select(e => new {e.id, e.text, e.start_date, e.end_date, e.WorkplaceId)
           .ToList()
        );
    return (ContentResult)data;
}

Was this article helpful?

Yes No