Gantt Chart for ASP.NET MVC with dhtmlxGantt

| Comments (22)

This tutorial will lead you through the steps required to integrate dhtmlxGantt into an ASP.NET MVC application. dhtmlxGantt is an open source (GPL) JavaScript library that draws attractive Gantt charts and allows the end users to edit the tasks in a convenient way. We will learn how to put this Ajax-based Gantt chart on a web page, load tasks from the .NET server side, and update them in the database when a user makes changes in the browser.

Gantt Chart for ASP.NET MVC - Demo

If you are familiar with ASP.NET MVC and dhtmlxGantt and you just want to dive into the code, download the final demo app.

NOTE: The sample goes without pre-downloaded NuGet packages. They will be installed automatically when you Build the project in Visual Studio. If it doesn’t happen, you can either configure Visual Studio to restore missing packages during the build, or install them manually by this command in Package Manager Console:

 >> Update-Package-Reinstall

If you need the detailed instructions on how to use dhtmlxGantt with ASP.NET MVC, keep reading the tutorial.

Getting Started

First of all, you need to create a new MVC project.

dhtmlxGantt with ASP.NET MVC - Creating a Project

The next things you need are dhtmlxGantt and EntityFramework libraries. You can install them using Nuget Package Manager.

Once all necessary libraries are installed you can proceed to the first step.

Step 1 – Initialization

First of all, you need to add links to the dhtmlxGantt files. Open the _Layout.cshtml file and modify it as follows:

<!DOCTYPE html>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="~/Scripts/dhtmlxgantt/dhtmlxgantt.js" type="text/javascript" charset="utf-8"></script>
        <link rel="stylesheet" href="~/Content/dhtmlxgantt/dhtmlxgantt.css" type="text/css" />  
    <style type="text/css">
html, body
    height: 100%;
    padding: 0px;
    margin: 0px;
    overflow: hidden;


    <script src="~/Scripts/main.js" type="text/javascript" charset="utf-8"></script>


Then create HomeController with Index action. Add this HTML container for dhtmlxGantt to the Index view:

<div id="ganttContainer" style="width: 100%; height: 100%;"></div>

After that we need to add code that initializes the Gantt chart on a page. Create main.js file in the Scripts folder and fill it with the following data:

(function () {
    // add month scale
    gantt.config.scale_unit = "week";
    gantt.config.step = 1;
    gantt.templates.date_scale = function (date) {
        var dateToStr ="%d %M");
        var endDate =, 1, "week"), -1, "day");
        return dateToStr(date) + " - " + dateToStr(endDate);
    gantt.config.subscales = [
        { unit: "day", step: 1, date: "%D" }
    gantt.config.scale_height = 50;

    // configure milestone description
    gantt.templates.rightside_text = function (start, end, task) {
        if (task.type == gantt.config.types.milestone) {
            return task.text;
        return "";
    // add section to type selection: task, project or milestone
    gantt.config.lightbox.sections = [
        { name: "description", height: 70, map_to: "text", type: "textarea", focus: true },
        { name: "type", type: "typeselect", map_to: "type" },
        { name: "time", height: 72, type: "duration", map_to: "auto" }

    gantt.config.xml_date = "%Y-%m-%d %H:%i:%s"; // format of dates in XML
    gantt.init("ganttContainer"); // initialize gantt

In this code we’ve specified scales configuration – two line scale that will display days and weeks. Also we defined labels text for milestones and sections of the task details form. At the end we’ve specified the format of dates for data loading (this is needed to parse the server-side data correctly) and initialized our Gantt chart.

At this point, the running application will look like this (no data yet):

dhtmlxGantt with ASP.NET MVC - Empty Chart

Step 2 – Creating Task and Link Models

Now we need to create models to store our tasks and links. In this sample we use EF Code First, so you don’t need to create database tables manually. Let’s create Task and Link classes in Model folder:

public class GanttTask
        public int GanttTaskId { get; set; }
        public string Text { get; set; }
        public DateTime StartDate { get; set; }
        public int Duration { get; set; }
        public decimal Progress { get; set; }
        public int SortOrder { get; set; }
        public string Type { get; set; }
        public int? ParentId { get; set; }

public class GanttLink
        public int GanttLinkId { get; set; }
        public string Type { get; set; }
        public int SourceTaskId { get; set; }
        public int TargetTaskId { get; set; }

Note that classes for Tasks and Links can have any number of custom columns that can be accessed on the client-side.

The next step is to create the database context class. Create a new folder DAL (for Data Access Layer). In that folder create a new class file named GanttContext.cs, and replace the template code with the following code:

public class GanttContext : DbContext
        public GanttContext() : base("GanttContext") { }

        public DbSet<GanttTask> GanttTasks { get; set; }
        public DbSet<GanttLink> GanttLinks { get; set; }

In this class, we link our models with database. The context class will use connection string named “GanttContext”, so you’ll need to define one in order to fetch the database.

Step 3 – Loading Data

Now it came the turn of loading data. The client-side dhtmlxGantt component uses simple JSON structure as described here. Basically it is an object with two array properties, one for the links and one for the tasks. Task dates should be serialized in format specified in gantt.config.xml_date, that has been defined in the main.js. We need to create a JSON object with two arrays for Tasks and Links.

Add Data action to HomeController.cs and GanttContext variable to access the database:

// database access
private readonly GanttContext db = new GanttContext();

public JsonResult GetGanttData()
            var jsonData = new
                // create tasks array
                data = (
                    from t in db.GanttTasks.AsEnumerable()
                    select new
                        id = t.GanttTaskId,
                        text = t.Text,
                        start_date = t.StartDate.ToString("u"),
                        duration = t.Duration,
                        order = t.SortOrder,
                        progress = t.Progress,
                        open = true,
                        parent = t.ParentId,
                        type = (t.Type != null) ? t.Type : String.Empty
                // create links array
                links = (
                    from l in db.GanttLinks.AsEnumerable()
                    select new
                        id = l.GanttLinkId,
                        source = l.SourceTaskId,
                        target = l.TargetTaskId,
                        type = l.Type

            return new JsonResult { Data = jsonData, JsonRequestBehavior = JsonRequestBehavior.AllowGet };

Then we need to configure dhtmlxGantt to load data from this actions. Add the following line after the grid initialization in the main.js file:

gantt.load("/Home/Data", "json");

Now dhtmlxGantt is able to load data. However our database is empty so let’s configure entity framework to initialize the database with test data. In the DAL folder, create a new class file named GanttInitializer.cs and replace the template code with the following code, which causes a database to be created when needed and loads test data into the new database:

   public class GanttInitializer : DropCreateDatabaseIfModelChanges<GanttContext>
        protected override void Seed(GanttContext context)
            List<GanttTask> tasks = new List<GanttTask>()
                new GanttTask() { GanttTaskId = 1, Text = "Project #2", StartDate = DateTime.Now.AddHours(-3),
                    Duration = 18, SortOrder = 10, Progress = 0.4m, ParentId = null },
                new GanttTask() { GanttTaskId = 2, Text = "Task #1", StartDate = DateTime.Now.AddHours(-2),
                    Duration = 8, SortOrder = 10, Progress = 0.6m, ParentId = 1 },
                new GanttTask() { GanttTaskId = 3, Text = "Task #2", StartDate = DateTime.Now.AddHours(-1),
                    Duration = 8, SortOrder = 20, Progress = 0.6m, ParentId = 1 }

            tasks.ForEach(s => context.GanttTasks.Add(s));

            List<GanttLink> links = new List<GanttLink>()
                new GanttLink() { GanttLinkId = 1, SourceTaskId = 1, TargetTaskId = 2, Type = "1" },
                new GanttLink() { GanttLinkId = 2, SourceTaskId = 2, TargetTaskId = 3, Type = "0" }

            links.ForEach(s => context.GanttLinks.Add(s));

The Seed method takes the database context object as an input parameter, and the code in the method uses
that object to add new entities to the database. For each entity type, the code creates a collection of new entities, adds them to the appropriate DbSet property, and then saves the changes to the database.

To tell Entity Framework to use your initializer class, add an element to the entityFramework element in the application Web.config file, as shown in the following example:

    <context type="Gantt.DAL.GanttContext, Gantt">
        <databaseInitializer type="Gantt.DAL.GanttInitializer, Gantt" />

dhtmlxGantt is now ready for loading data. If you run your application, it will look like this:

dhtmlxGantt with ASP.NET MVC - Gantt Chart with Data

Step 4 – Saving Changes to the Database

After all the steps described above, we’ve built a Gantt chart that loads the tasks from the database but that’s not enough. Our next goal is to save changes made on the client side to the server. For this we will use dataProcessor on the client. This library is integrated into dhtmlxGantt and automatically traces the changes made on the client side and sends updates to the server, so we need to configure the server to handle these requests.

First of all, we need a class which will parse and represent data action sent from the client. Create a new GanttRequest model in the Model folder and add the following code:

public enum GanttMode

public enum GanttAction

public static List<GanttData> Parse(FormCollection form, string ganttMode)
            // save current culture and change it to InvariantCulture for data parsing
            var currentCulture = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

            var ganttDataCollection = new List<GanttData>();
            var prefixes = form["ids"].Split(',');

            foreach (var prefix in prefixes)
                var ganttData = new GanttData();

                // lambda expression for form data parsing
                Func<string, string> parse = x => form[String.Format("{0}_{1}", prefix, x)];

                ganttData.Mode = (GanttMode)Enum.Parse(typeof(GanttMode), ganttMode, true);
                ganttData.Action = (GanttAction)Enum.Parse(typeof(GanttAction), parse("!nativeeditor_status"), true);
                ganttData.SourceId = Int64.Parse(parse("id"));

                // parse gantt task
                if (ganttData.Action != GanttAction.Deleted && ganttData.Mode == GanttMode.Tasks)
                    ganttData.GanttTask = new GanttTask()
                        GanttTaskId = (ganttData.Action == GanttAction.Updated) ? (int)ganttData.SourceId : 0,
                        Text = parse("text"),
                        StartDate = DateTime.Parse(parse("start_date")),
                        Duration = Int32.Parse(parse("duration")),
                        Progress = Decimal.Parse(parse("progress")),
                        ParentId = (parse("parent") != "0") ? Int32.Parse(parse("parent")) : (int?)null,
                        SortOrder = (parse("order") != null) ? Int32.Parse(parse("order")) : 0,
                        Type = parse("type")
                // parse gantt link
                else if (ganttData.Action != GanttAction.Deleted && ganttData.Mode == GanttMode.Links)
                    ganttData.GanttLink = new GanttLink()
                        GanttLinkId = (ganttData.Action == GanttAction.Updated) ? (int)ganttData.SourceId : 0,
                        SourceTaskId = Int32.Parse(parse("source")),
                        TargetTaskId = Int32.Parse(parse("target")),
                        Type = parse("type")


            // return current culture back
            Thread.CurrentThread.CurrentCulture = currentCulture;

            return ganttDataCollection;

GanttRequest class has several properties to store the action info that came from the client side, and one method that creates a collection of data actions from the request values.

The data is sent from the client in the following format:

13_text: "New task"
13_duration: 1

which is common for all DHTMLX components that use dhtmlxDataprocessor.

A single request may specify operations on several data items. Each property has prefix that links to an id of the related data item. All sent ids are stored in “ids” parameter and separated by commas:


By default, requests for insert/update contains only one id, while requests for deleting may have multiple items specified. Although, in general case, the request may contain any number of operations of different kind.

First, we change the current culture to InvariantCulture. This is needed for more predictable parsing of request parameters – the format of dates and numbers that comes from a client-side component does not depends on the server-side culture settings. Then we parse request values into collection of individual data actions and store it in request variable (GanttRequest class has been defined in Models/GanttRequest.cs).

Note that the type of data action is defined by two properties:

  • Mode – specifies the data entity, it can be a link or a task
  • Actions – specifies the type of an operation, can be delete, update, insert

Depending on action and mode, we populate UpdatedTask or UpdatedLink objects from the request values.

Now, depending on the action, we need to save the changes. For this, we will create in HomeController.cs Save action, two private methods: UpdateTasks and UpdateLinks. We also create GanttRespose method for XML response.

        public ContentResult UpdateGanttData(FormCollection form)
            var ganttDataCollection = GanttData.Parse(form, Request.QueryString["gantt_mode"]);
                foreach (var ganttData in ganttDataCollection)
                    switch (ganttData.Mode)
                        case GanttMode.Tasks:
                        case GanttMode.Links:
                // return error to client if something went wrong
                ganttDataCollection.ForEach(g => { g.Action = GanttAction.Error; });
            return GanttRespose(ganttDataCollection);

        private void UpdateGanttTasks(GanttData ganttData)
            switch (ganttData.Action)
                case GanttAction.Inserted:
                    // add new gantt task entity
                case GanttAction.Deleted:
                    // remove gantt tasks
                case GanttAction.Updated:
                    // update gantt task
                    ganttData.Action = GanttAction.Error;

        private void UpdateGanttLinks(GanttData ganttData)
            switch (ganttData.Action)
                case GanttAction.Inserted:
                    // add new gantt link
                case GanttAction.Deleted:
                    // remove gantt link
                case GanttAction.Updated:
                    // update gantt link
                    ganttData.Action = GanttAction.Error;

       private ContentResult GanttRespose(List<GanttData> ganttDataCollection)
            var actions = new List<XElement>();
            foreach (var ganttData in ganttDataCollection)
                var action = new XElement("action");
                action.SetAttributeValue("type", ganttData.Action.ToString().ToLower());
                action.SetAttributeValue("sid", ganttData.SourceId);
                action.SetAttributeValue("tid", (ganttData.Action != GanttAction.Inserted) ? ganttData.SourceId :
                    (ganttData.Mode == GanttMode.Tasks) ? ganttData.GanttTask.GanttTaskId : ganttData.GanttLink.GanttLinkId);

            var data = new XDocument(new XElement("data", actions));
            data.Declaration = new XDeclaration("1.0", "utf-8", "true");
            return Content(data.ToString(), "text/xml");

In the Save action we parse the request values into collection of individual data actions. Then for each data action, depending on it’s mode, we perform update on links or tasks table.

After updates are done, we need to return the response for the client side. Method GanttRespose renders the XML response that will notify the client-side about the result of operation (success or error).

UpdateGanttTaks and UpdateLinks methods are fairly simple. They call Entity Framework function to update/insert/delete new tasks or links.

XML response format is also very simple:

insert: <data><action type="inserted" sid="temp id" tid="new id from database" /></data>
update: <data><action type="updated" sid="entity id" tid="entity database" /></data>
delete: <data><action type=”deleted” sid=”first entity id” tid=”first entity id” /><action type=”deleted” sid=”second entity id” tid=”second entity id” /></data>

We are almost done. The last thing left to do is to activate the dataProcessor on the client. Add the following lines to the end of the main.js file:

// enable dataProcessor
var dp = new dataProcessor("/Home/Save");

Now, if you run the application after these updates, you can create/update/delete tasks and links on your Gantt chart.

dhtmlxGantt with ASP.NET MVC - Final Demo

If you followed this tutorial, you now have built an interactive Gantt chart with ASP.NET MVC backend. The end users can create and manage tasks using an intuitive drag-and-drop interface. All the changes are saved to the server and updated in the database. Using dhtmlxGantt API, you can extend the functionality of your app and customize it further to your needs. Hope, this basic example will be a good help for you.

Download the final demo app to better understand this integration of dhtmlxGantt with ASP.NET MVC.


  1. Stephen S. June 19, 2014 at 9:34 pm

    Hello all, i just downloaded the standard version of DhtmlxGantt, I am trying to incorporate it in my ASP.Net MVC application and it is not working.

    I followed the tutorial with every step and it doesn’t seem to work.
    It is a MVC5 application using the Razor view engine.

    I do have some other stuff on the same page as the Gantt, but even when trying to put it on its seperate page, it does not show up.

    when I tried the sample file provided for download, it did work.

    • Craig cocker June 25, 2014 at 8:55 am

      Yes – I have just tried the same thing with MVC 5 application and it does not seem to compile the GanntRequest.

      Also when downloading the finished app – it fails to load. I have tried it in VS2012 & 2013. I also tried the install-package-reinstall but that failed also

  2. Rajeev Raina August 20, 2014 at 5:08 pm

    I need to add some editable custom columns. Do you have any step-by-step instructions for the same.

  3. Jose March 30, 2015 at 9:34 pm

    the following “error” is shown at the end of step 1: “invalid html container: ganttContainer”
    what could it be?

    • Ivan (DHTMLX team) March 31, 2015 at 4:25 pm

      Hi Jose, seems like element with id ‘ganttContainer’ does not exist when gantt is initialized.
      Make sure you’ve added div with such id and that gantt.init is called after it’s loaded to the page – either put main.js below this div, or call it’s code from window.onload.

  4. Jose April 2, 2015 at 9:01 pm

    Hi, Ivan. Thanks for the reply.
    I have the div id ganttContainer and now i put the main.js below it, the gantt.init is calling the id “ganttContainer” but the error persists.
    I figured out this error only appears when i’m not in my gantt view, but in any other view. Is it any mistake of mine?
    Thanks again.

  5. Jose April 2, 2015 at 9:13 pm

    Ivan, thanks once again. I’ve just asked you, but I solved this question.
    The Problem is: i put the “main.js” below my @renderBody. It made each “view” using this js file and the error ocurred.
    I’m gonna try the Gantt, and I hope we do business together.

  6. Mathew May 7, 2015 at 12:24 pm

    How do i get reordering to work on MVC, gives me a “Requested value ‘order’ was not found.” error.

    • Russ September 4, 2015 at 5:45 am

      Hi Matthew, any luck sorting this out? I’ve been working on this problem today. I got as far as determining that the GanttAction being returned by the DataProcessor is ‘order’, which is not one of the enum values listed in the TERRIBLY CONFUSING blog post on MVC integration. If you add a fifth value of ‘order’ to the GanttAction enum (along with adding a “case GanttAction.order:” clause to the switch in the UpdateTasks action) then you will at least be able to avoid the “Requested value…” error, but this doesn’t really get you any closer to a solution. With the changes listed above I’m now able to manually edit the SortOrder values directly in the database (i.e. 1, 2, 3 , 4…), and I’ve confirmed that the values are maintained throughout a round trip to the client and back, but these values don’t seem to influence the task order in the rendered chart . The other problem I’m having is that re-ordering the tasks doesn’t actually influence the ‘order’ values that get sent back to the server by the DataProcessor; whatever values were retrieved from the database are returned to the server unchanged.

  7. Ash May 12, 2015 at 7:22 pm

    How can I assign resources to the tasks?
    I’m learning mvc5 on my own and I’m finding it difficult

  8. Alex June 3, 2015 at 4:35 pm

    I am on IE 10. I ran this sample project in VS2012 pro. IE doesn’t seem to refresh correctly. What I meant is something like following

    #1 On VS2012, press F5 to start debugging
    #2 Gantt chart filled with sample data, so far so good
    #3 Add a new task and click Save
    #4 Gantt chart was updated correctly with my new task, great!
    #5 When I click refresh on IE, the new task didn’t show up. I checked local SQL server db, my new task was inserted successfully.

    Any idea? Thanks.

  9. Konrad October 24, 2015 at 2:40 pm

    I have a problem with Step 3. I’ve done everything you said, but in result i’ve got no Tasks in my view. I’ve the newest version of dhtmlxGantt, downloaded from Nuget and there is no dhtmlxgantt.css file. I add my context to section. Should i worry? What am i doing wrong?

  10. Tommy November 20, 2015 at 1:14 pm

    I can`t seem to get the tasks into the diagram.

  11. Dammy April 4, 2016 at 11:44 pm

    hi, how is the progress field in the database calculated?

    • Maria (DHTMLX team) July 22, 2016 at 4:00 pm

      Hi Dammy,
      no calculation is performed by default. When you specify the progress value on the client side it is saved to the db as is.
      If you want to calculate a combined progress of a summary task or of a whole project, you can do it using a public api. E.g. check this demo

  12. Wally April 22, 2016 at 6:46 am

    is Hours Scale view possible on standard edition?

  13. Maria (DHTMLX team) April 22, 2016 at 11:59 am

    Hi Wally, yes, Hours Scale is included in the Standard functionality of Gantt.

  14. Steve July 22, 2016 at 2:59 am

    How can fix this error: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

    • Maria (DHTMLX team) July 22, 2016 at 3:54 pm

      Check the output of GetGanttData() action in “Step 3 – Loading Data”.
      The error says it returns an invalid JSON, probably there is some kind of a runtime error.

      • Steve July 22, 2016 at 7:06 pm

        Thanks works… But now I have another problem. Change these lines new GanttTask() { GanttTaskId = 1, Text = “A”, StartDate = DateTime.Today.AddDays(-3), Duration = 4, SortOrder = 2, Progress = 0.4m, ParentId = null } But but the browser shows me Project 1, Duration 18…

        • Maria (DHTMLX team) July 22, 2016 at 7:25 pm

          Steve, could you please describe your problem and post it to the forum You’ll get help faster in this way.

Leave a Reply