Get Started
- 16 minutes to read
This tutorial shows how to use the GanttControl — how to populate it with data, specify the workweek schedule, allow users to modify tasks in the chart, enable a modal form to edit tasks in the task list, and how to customize task appearance.
Add the Control to a Form
Create a new project in Visual Studio. Drop the GanttControl from the Toolbox window onto the main application form.
In the control’s smart tag menu, click Create Ribbon and Dock in Parent Container to add the ribbon with chart commands to the form and make the Gantt control fill all the available space.
Populate the Control with Data
You can bind the control to a data source in code or in the designer. Use the following properties to assign a data source to the control:
DataSource — specifies the data source that contains tasks. The TreeListMappings and ChartMappings specify data source fields that contain the following data:
- KeyFieldName, ParentFieldName (of the Object type) — the task ID and its parent ID. These identifiers are used to create the tree structure.
- TextFieldName (of the String type) — the text displayed next to the task in the chart.
- StartDateFieldName, FinishDateFieldName (of the DateTime type) — the task’s start and finish dates.
- DurationFieldName (of the TimeSpan type) - the task duration.
- PredecessorsFieldName (of the Object type) — the task’s predecessors. It starts when the latest predecessor finishes. This field supports finish-to-start dependencies only.
DependencySource — specifies the data source that contains dependencies. This data source allows you to support all dependency types. The DependencyMappings specify data source fields that contain the following data:
- PredecessorFieldName (of the Object type) — the predecessor task’s ID.
- SuccessorFieldName (of the Object type) — the successor task’s ID.
- TypeFieldName (of the DependencyType type) — the dependency type (finish-to-start, finish-to-finish, etc.).
- LagFieldName (of the TimeSpan type) — the time delay between the predecessor and the successor.
The GanttControl supports the same binding modes as the TreeList. See Data Binding for details on how to bind the TreeList to a data source. For more information about mapping to data source fields in the GanttControl, see the Bind to Data Source topic. For general information about binding DevExpress controls to data sources, see Data Binding Common Concepts.
This tutorial shows how to bind the control to a sample business object and data exported from Microsoft Project.
Note
Make sure that you specified all required mappings. Otherwise, specific functionality may not be available. See the following topic for more information: Bind to Data Source.
Important
If you use all three time-related task properties (StartDateFieldName, FinishDateFieldName, Duration), make sure the Duration does not conflict with Start and Finish dates. Since the control draws tasks depending on Start and Finish dates, these conflicts can be masked until you try to edit a task. For example:
- StartDate — February 10, 2021, 1:00 p.m.
- FinishDate — February 11, 2021, 1:00 p.m.
- Duration — 24 hours (1 day)
- ScheduleMode — Auto
- Work day duration — 8 hours
This setup introduces a conflict: the 24-hour task duration means a task should span across three days (eight working hours a day), which mismatches StartDate-FinishDate values. The control will draw a 24-hour task based on Start and Finish dates, but the 3-day duration will be stored internally, and will cause issues when a task is edited.
Business Objects
You can use business objects to store data. The example below creates a collection of Task objects and assigns it to the DataSource property. The data source also contains information about finish-to-start dependencies between tasks.
The resulting application window is shown in the figure below.
using System;
using System.ComponentModel;
using System.Collections.Generic;
ganttControl1.TreeListMappings.KeyFieldName = "Id";
ganttControl1.TreeListMappings.ParentFieldName = "ParentId";
ganttControl1.ChartMappings.TextFieldName = "Name";
ganttControl1.ChartMappings.StartDateFieldName = "StartDate";
ganttControl1.ChartMappings.FinishDateFieldName = "FinishDate";
ganttControl1.ChartMappings.DurationFieldName = "Duration";
ganttControl1.ChartMappings.PredecessorsFieldName = "Predecessors";
ganttControl1.ChartMappings.ProgressFieldName = "Progress";
ganttControl1.DataSource = LoadData();
public static IList<Task> LoadData() {
var tasks = new List<Task>();
Task softwareDevelopment = new Task("Software Development", 0, -1, DateTime.Now, 1, 24);
Task analyseRequirements = new Task("Analyse Requirements", 1, softwareDevelopment.Id, softwareDevelopment.StartDate, 1, 100);
Task developFunctionalSpecifications = new Task("Develop functional specifications", 2, softwareDevelopment.Id, analyseRequirements.FinishDate, 1, 100, 1);
Task developSoftware = new Task("Develop software", 3, softwareDevelopment.Id, developFunctionalSpecifications.FinishDate, 5, 40, developFunctionalSpecifications.Id);
Task developHelpSystem = new Task("Develop help system", 4, softwareDevelopment.Id, developFunctionalSpecifications.FinishDate, 1, 90, developFunctionalSpecifications.Id);
Task developUserManuals = new Task("Develop user manuals", 5, softwareDevelopment.Id, developHelpSystem.FinishDate, 1, 0, developHelpSystem.Id);
Task testSoftware = new Task("Test software", 6, softwareDevelopment.Id, developSoftware.FinishDate, 2, 0, developSoftware.Id);
Task deployBeta = new Task("Deploy Beta", 7, softwareDevelopment.Id, testSoftware.FinishDate, 0, 0, testSoftware.Id);
Task collectFeedback = new Task("Collect feedback", 8, softwareDevelopment.Id, deployBeta.FinishDate, 2, 0, deployBeta.Id);
Task fixBugs = new Task("Fix bugs", 9, softwareDevelopment.Id, collectFeedback.FinishDate, 2, 0, collectFeedback.Id);
Task incorporateFeedBack = new Task("Incorporate feedback", 10, softwareDevelopment.Id, collectFeedback.FinishDate, 3, 0, collectFeedback.Id);
Task releaseSoftware = new Task("Release software", 11, softwareDevelopment.Id, incorporateFeedBack.FinishDate, 2, 0, fixBugs.Id, incorporateFeedBack.Id);
Task createSoftwareMaintenanceTeam = new Task("Create software maintenance team", 12, softwareDevelopment.Id, deployBeta.FinishDate, 1, 0, developSoftware.Id);
Task softwareDevelopmentComplete = new Task("Software development complete", 13, softwareDevelopment.Id, releaseSoftware.FinishDate, 0, 0, releaseSoftware.Id);
softwareDevelopment.FinishDate = softwareDevelopmentComplete.FinishDate;
tasks.AddRange(new Task[] {softwareDevelopment, analyseRequirements, developFunctionalSpecifications, developSoftware, developHelpSystem, developUserManuals,
testSoftware,deployBeta,collectFeedback, fixBugs, incorporateFeedBack, releaseSoftware, createSoftwareMaintenanceTeam, softwareDevelopmentComplete });
return tasks;
}
public class Task{
public Task(string name, int id, int parentId, DateTime start, int duration, double progress, params int[] predecessors) {
Name = name;
Id = id;
ParentId = parentId;
StartDate = start;
Duration = TimeSpan.FromDays(duration);
FinishDate = start + Duration;
Progress = progress;
Predecessors = new BindingList<int>();
foreach (int predecessor in predecessors) {
Predecessors.Add(predecessor);
}
}
public int Id { get; set; }
public int ParentId { get; set; }
public BindingList<int> Predecessors { get; private set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime FinishDate { get; set; }
public TimeSpan Duration { get; set; }
public double Progress { get; set; }
}
Data from Microsoft Project
To use data from Microsoft Project, export data to a file and then convert the data to an appropriate format. In this example, data is stored in a DataTable. To store dependencies, this example uses a separate data source assigned to the DependencySource property.
Export Data from Microsoft Project
Save a project as a tab-delimited text file.
Tip
You may need to enable legacy formats in the Trust Center window to enable export to text files. To open this window, click File - Options - Trust Center - Trust Center Settings…
Select tasks. Do not include headers. Click Next.
Select data fields to export in the following order: ID, WBS (work breakdown structure), Name, Start, Finish, Predecessors, Resource Names. Click Finish.
This tutorial uses the Software Development Plan template from Microsoft Project. The first three lines contain the following data:
0 0 Software Development 4/21/20 8:00 AM 9/1/20 3:00 PM
1 1 Scope 4/21/20 8:00 AM 4/24/20 12:00 PM
2 1.1 Determine project scope 4/21/20 8:00 AM 4/21/20 12:00 PM Management
Import Data to a Table in Code
Use the following code to create tables that contain data from the file. The resulting application window is shown in the figure below.
using DevExpress.XtraGantt;
using System;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
// Specify data fields that contain
// information about tasks required for
// the control to display the project.
ganttControl1.TreeListMappings.KeyFieldName = "ID";
ganttControl1.TreeListMappings.ParentFieldName = "ParentID";
ganttControl1.ChartMappings.StartDateFieldName = "StartDate";
ganttControl1.ChartMappings.FinishDateFieldName = "FinishDate";
ganttControl1.ChartMappings.TextFieldName = "Resources";
// Specify data fields that contain
// information about dependencies between tasks.
ganttControl1.DependencyMappings.PredecessorFieldName = "PredecessorID";
ganttControl1.DependencyMappings.SuccessorFieldName = "SuccessorID";
ganttControl1.DependencyMappings.TypeFieldName = "Type";
// Specify the path to the exported project.
string path = "c:\\export\\project1.txt";
DataTable tasks = new DataTable();
DataTable dependencies = new DataTable();
PopulateDataSources(path, ref tasks, ref dependencies);
ganttControl1.DataSource = tasks;
ganttControl1.DependencySource = dependencies;
public static void PopulateDataSources(string path, ref DataTable tasks, ref DataTable dependencies) {
// Create columns for the table that contains tasks.
DataColumn colId = new DataColumn("ID", typeof(string));
DataColumn colParentId = new DataColumn("ParentID", typeof(string));
DataColumn colText = new DataColumn("Text", typeof(string));
DataColumn colStart = new DataColumn("StartDate", typeof(DateTime));
DataColumn colFinish = new DataColumn("FinishDate", typeof(DateTime));
DataColumn colDuration = new DataColumn("Duration", typeof(TimeSpan));
DataColumn colResources = new DataColumn("Resources", typeof(string));
DataColumn colWbs = new DataColumn("WBS", typeof(string));
tasks.Columns.AddRange(new DataColumn[] { colId, colParentId, colText, colStart, colFinish, colDuration, colResources, colWbs });
// Create columns for the table that contains dependencies.
DataColumn colPredecessor = new DataColumn("PredecessorID", typeof(string));
DataColumn colSuccessor = new DataColumn("SuccessorID", typeof(string));
DataColumn colType = new DataColumn("Type", typeof(DevExpress.XtraGantt.DependencyType));
dependencies.Columns.AddRange(new DataColumn[] { colPredecessor, colSuccessor, colType });
// Read from the file.
using (StreamReader sr = new StreamReader(path)) {
while (sr.Peek() >= 0) {
string line = sr.ReadLine();
string[] values = line.Split('\t');
string taskId = values[0];
// Get parent ID for each task
// from the work breakdown structure
// used in MS Project.
string taskParentId = string.Empty;
string[] pathWbs = values[1].Split('.');
if (pathWbs.Length == 1)
taskParentId = "0";
else {
string parentWbs = string.Join(".", pathWbs.Take(pathWbs.Length - 1));
foreach (DataRow row in tasks.Rows) {
string rowWbs = (string)row[colWbs];
if (rowWbs == parentWbs) {
taskParentId = (string)row[colId];
}
}
}
// Convert strings to DateTime structures.
DateTime taskStartDate = DateTime.Parse(values[3], CultureInfo.CurrentCulture);
DateTime taskFinishDate = DateTime.Parse(values[4], CultureInfo.CurrentCulture);
// This example calculates the duration
// instead of importing it from the file.
TimeSpan taskDuration = taskFinishDate - taskStartDate;
// Add a new row with the values to the table.
tasks.Rows.Add(new object[] {
taskId,
taskParentId,
values[2], // Text.
taskStartDate,
taskFinishDate,
taskDuration,
values[6], // Resource Names.
values[1] // Work breakdown structure.
});
string[] taskPredecessors = taskPredecessors = values[5].Split(',');
if (taskPredecessors.Length > 0) {
foreach (string value in taskPredecessors) {
DependencyType dependencyType = DependencyType.FinishToStart;
string predecessorId = value;
if (value.Contains("FS")) {
dependencyType = DependencyType.FinishToStart;
predecessorId = value.Substring(0, value.IndexOf("FS"));
// This example ignores the time lag.
string timeLag = value.Substring(value.IndexOf("FS") + 2, value.Length - value.IndexOf("FS") - 2);
}
if (value.Contains("FF")) {
dependencyType = DependencyType.FinishToFinish;
predecessorId = value.Substring(0, value.IndexOf("FF"));
}
if (value.Contains("SS")) {
dependencyType = DependencyType.StartToStart;
predecessorId = value.Substring(0, value.IndexOf("SS"));
}
if (value.Contains("SF")) {
dependencyType = DependencyType.StartToFinish;
predecessorId = value.Substring(0, value.IndexOf("SF"));
}
dependencies.Rows.Add(new object[] { predecessorId, taskId, dependencyType });
}
}
}
}
}
Edit Tasks
Users can use cell editors to edit tasks in the task list.
You can enable a modal form instead of cell editors or allow users to edit tasks in the chart.
Edits in Chart
To allow users to modify tasks in the chart, enable the following options:
- AllowModifyTasks — to reschedule a task.
- AllowModifyProgress — to update a task’s progress. See the following topic for more information: Task Progress.
- AllowModifyDependencies — to change a task’s predecessor or successor. See the following topic for more information: Task Dependencies.
ganttControl1.OptionsCustomization.AllowModifyTasks = DevExpress.Utils.DefaultBoolean.True;
ganttControl1.OptionsCustomization.AllowModifyProgress = DevExpress.Utils.DefaultBoolean.True;
ganttControl1.OptionsCustomization.AllowModifyDependencies = DevExpress.Utils.DefaultBoolean.True;
Respond to Task Changes
When a user modifies a task, the control raises events. You can handle these events to customize the operation. For example, the TaskMoveCompleted event fires when a user finishes moving a task to a new time slot, and allows you to cancel the operation. The code below shows how to ask for confirmation when a user moves a task beyond the project bounds.
ganttControl1.TaskMoveCompleted += GanttControl1_TaskMoveCompleted;
private void GanttControl1_TaskMoveCompleted(object sender, TaskMovingEventArgs e) {
if (e.CurrentTaskStart < ganttControl1.Nodes[0].GetStartDate())
e.Cancel = XtraMessageBox.Show(String.Format("You moved '{0}' to start before the project starts. Do you want to proceed?", e.ProcessedTask.Text), "Planning Wizard", MessageBoxButtons.YesNo) != DialogResult.Yes;
if (e.CurrentTaskFinish > ganttControl1.Nodes[0].GetFinishDate())
e.Cancel = XtraMessageBox.Show(String.Format("You moved '{0}' to finish after the project finishes. Do you want to proceed?", e.ProcessedTask.Text), "Planning Wizard", MessageBoxButtons.YesNo) != DialogResult.Yes;
}
See the following topic for more information: Interactive Editing.
Edits in Modal Form
To enable a modal form instead of cell editors, set the EditingMode property to EditForm. You can also customize the form layout or use a custom form.
ganttControl1.OptionsBehavior.EditingMode = DevExpress.XtraTreeList.TreeListEditingMode.EditForm;
ganttControl1.OptionsEditForm.FormCaptionFormat = "Summary Task Information - {Text}";
ganttControl1.Columns["StartDate"].OptionsEditForm.StartNewRow = true;
ganttControl1.Columns["Text"].OptionsEditForm.VisibleIndex = -2;
ganttControl1.Columns["Duration"].OptionsEditForm.VisibleIndex = -1;
ganttControl1.Columns["Text"].OptionsEditForm.UseEditorColRowSpan = false;
ganttControl1.Columns["Text"].OptionsEditForm.ColumnSpan = 2;
ganttControl1.Columns["Resources"].OptionsEditForm.UseEditorColRowSpan = false;
ganttControl1.Columns["Resources"].OptionsEditForm.ColumnSpan = 3;
ganttControl1.Columns["Resources"].OptionsEditForm.StartNewRow = true;
See the following topic for more information: Edit Form.
Automatic Scheduling
If the ScheduleMode property is set to Auto, the control automatically reschedules successor tasks when a user modifies a particular task. Set this property to Manual to disable automatic scheduling.
ganttControl1.OptionsBehavior.ScheduleMode = DevExpress.XtraGantt.Options.ScheduleMode.Auto;
Workweek Schedule and Holidays
The control takes the workweek schedule and holidays into account when it reschedules tasks. To specify the workweek schedule and holidays, click Run Designer in the control’s smart tag menu and switch to the Work Week Schedule section.
See the following topic for more information: Workweek Schedule and Exceptions.
Customize Task Appearance
The control gives you access to the drawing surface when a task is about to be drawn on-screen. You can draw a task or customize appearance settings. The code below shows how to customize the background color of tasks that missed a deadline.
DateTime deadLine = new DateTime(2020, 8, 26);
private void GanttControl1_CustomDrawTask(object sender, CustomDrawTaskEventArgs e) {
if (e.FinishDate > deadLine)
e.Info.Appearance.BackColor = Color.Red;
}
See the following topic for more information and code samples: Tasks, Summaries, and Milestones.