The xamGantt control is a cross platform control that provides a user interface similar to the Microsoft Project 2010.
You can create, edit and visualize your project plan, create deadlines, milestones and set dependencies between tasks using the xamGantt control. The control’s scheduling engine performs calculations on every edit to keep the plan up to date. In a series of several articles you can read the Infragistics Gantt Chart, beginning with an introduction and going to the details and tricks.
Probably the most important information for developers is how to start to use a specific control and how to integrate their applications. This blog is exactly as an introduction how to start working with Infragistics XamGantt control. I hope after this article programmers will have fun with this component.
Infragistics Gantt Control helps to create own project planning applications, that can contain most of the Microsoft Project features. Using Infragistics NetAdvantage for XAML (WPF and Silverlight) Vol. 12.2 you can create software for project-management teams providing the benefit from the next NetAdvantage updates of and features .
To be possible to start with Gantt Chart control you need to know how to:
- Add XamGantt to your application
- Gantt Control Data Binding
- Set Gant Chart Settings
- Create a Project
- Create Tasks
- using code
- using user interaction - Edit Task
- Delete Task
Adding XamGantt to your application
A project should has the following required references:
- InfragisticsWPF4.Controls.Editors.XamCalendar.dll
- InfragisticsWPF4.Controls.Grids.XamGrid.dll
- InfragisticsSL.Controls.Interactions.XamDialogWindow.dll (Only required for Silverlight projects)
- InfragisticsWPF4.Controls.Menus.XamMenu.dll
- InfragisticsWPF4.Controls.Schedules.dll
- InfragisticsWPF4.Controls.Schedules.XamGantt.dll
- InfragisticsWPF.DataManager.dll (Only required for WPF projects)
- InfragisticsWPF4.dll
If you want to use Gantt control from Visual Studio toolbox all references will be added automatically.
Infragistics Gantt Control Data Binding
The XamGantt control provides two options for data binding:
- Via the XamGantt Project property set to a Project instance
- Via the ListBackedProject and its TaskItemsSource property set to arbitrary custom tasks collection
Binding to Arbitrary Tasks Collection Using ListBackedProject
In many real life cases you can have own model for the tasks (for example when we have data stored in database and use ORM to create classes for your model) The XamGantt control can be bound to a collection of custom tasks. In this case, create an instance of ListBackedProject and set it to the XamGantt Project property.
To be possible to use custom tasks you need to:
- Create a custom task Model class
- Create a ViewModel class
- Create a ListBackedProject and bind it to data
- Create project task property to custom task property mappings
- Set the XamGantt Project property to the created ListBackedProject
Create a custom task Model class
Create an additional class named ObservableModel, that implements INotifyPropertyChanged
1: public class ObservableModel : INotifyPropertyChanged
2: {
3: #region NotifyPropertyChanged
4: public event PropertyChangedEventHandler PropertyChanged;
5: protected void NotifyPropertyChanged(String info)
6: {
7: if (PropertyChanged != null)
8: {
9: PropertyChanged(this, new PropertyChangedEventArgs(info));
10: }
11: }
12: #endregion NotifyPropertyChanged
13: }
Create a custom task model
1: class TaskModel : ObservableModel
2: {
3: #region ConstraintType
4: private ProjectTaskConstraintType _constraintType;
5: public ProjectTaskConstraintType ConstraintType
6: {
7: get
8: {
9: return _constraintType;
10: }
11: set
12: {
13: if (_constraintType != value)
14: {
15: _constraintType = value;
16: this.NotifyPropertyChanged("ConstraintType");
17: }
18: }
19: }
20: #endregion ConstraintType
21:
22: #region ConstraintDate
23: private DateTime _constraintDate;
24: public DateTime ConstraintDate
25: {
26: get
27: {
28: return _constraintDate;
29: }
30: set
31: {
32: if (_constraintDate != value)
33: {
34: _constraintDate = value;
35: this.NotifyPropertyChanged("ConstraintDate");
36: }
37: }
38: }
39: #endregion ConstraintDate
40:
41: #region DeadlineDate
42: private DateTime? _deadlineDate;
43: public DateTime? DeadlineDate
44: {
45: get
46: {
47: return _deadlineDate;
48: }
49: set
50: {
51: if (_deadlineDate != value)
52: {
53: _deadlineDate = value;
54: this.NotifyPropertyChanged("DeadlineDate");
55: }
56: }
57: }
58: #endregion //DeadlineDate
59:
60: #region DurationFormat
61: private ProjectDurationFormat _durationFormat;
62: public ProjectDurationFormat DurationFormat
63: {
64: get
65: {
66: return _durationFormat;
67: }
68: set
69: {
70: if (_durationFormat != value)
71: {
72: _durationFormat = value;
73: this.NotifyPropertyChanged("DurationFormat");
74: }
75: }
76: }
77: #endregion DurationFormat
78:
79: #region Duration
80: private TimeSpan _duration;
81: public TimeSpan Duration
82: {
83: get
84: {
85: return _duration;
86: }
87: set
88: {
89: if (_duration != value)
90: {
91: _duration = value;
92: this.NotifyPropertyChanged("Duration");
93: }
94: }
95: }
96: #endregion Duration
97:
98: #region IsMilestone
99: private bool _isMilestone = false;
100: public bool IsMilestone
101: {
102: get
103: {
104: return _isMilestone;
105: }
106: set
107: {
108: if (_isMilestone != value)
109: {
110: _isMilestone = value;
111: this.NotifyPropertyChanged("IsMilestone");
112: }
113: }
114: }
115: #endregion IsMilestone
116:
117: #region IsInProgress
118: private bool _isInProgress = true;
119: public bool IsInProgress
120: {
121: get
122: {
123: return _isInProgress;
124: }
125: set
126: {
127: if (_isInProgress != value)
128: {
129: _isInProgress = value;
130: this.NotifyPropertyChanged("IsInProgress");
131: }
132: }
133: }
134: #endregion IsInProgress
135:
136: #region IsUndetermined
137: private bool _isUndetermined = false;
138: public bool IsUndetermined
139: {
140: get
141: {
142: return _isUndetermined;
143: }
144: set
145: {
146: if (_isUndetermined != value)
147: {
148: _isUndetermined = value;
149: this.NotifyPropertyChanged("IsUndetermined");
150: }
151: }
152: }
153: #endregion //IsUndetermined
154:
155: #region Predecessors
156: private string _predecesors;
157: public string Predecessors
158: {
159: get { return _predecesors; }
160: set
161: {
162: if (value != _predecesors)
163: {
164: _predecesors = value;
165: this.NotifyPropertyChanged("Predecessors");
166: }
167: }
168: }
169: #endregion //Predecessors
170:
171: #region Successors
172: private string _successors;
173: public string Successors
174: {
175: get { return _successors; }
176: set
177: {
178: if (value != _successors)
179: {
180: _successors = value;
181: this.NotifyPropertyChanged("Successors");
182: }
183: }
184: }
185: #endregion //Successors
186:
187: #region Start
188: private DateTime _start;
189: public DateTime Start
190: {
191: get
192: {
193: return _start;
194: }
195: set
196: {
197: if (_start != value)
198: {
199: _start = value;
200: this.NotifyPropertyChanged("Start");
201: }
202: }
203: }
204: #endregion Start
205:
206:
207: #region TaskID
208: private string _taskId;
209: public string TaskID
210: {
211: get
212: {
213: return _taskId;
214: }
215: set
216: {
217: if (_taskId != value)
218: {
219: _taskId = value;
220: this.NotifyPropertyChanged("TaskID");
221: }
222: }
223: }
224: #endregion TaskID
225:
226: #region Tasks
227: private string _tasks;
228: public string Tasks
229: {
230: get
231: {
232: return _tasks;
233: }
234: set
235: {
236: if (_tasks != value)
237: {
238: _tasks = value;
239: this.NotifyPropertyChanged("Tasks");
240: }
241: }
242: }
243: #endregion Tasks
244:
245: #region TaskName
246: private string _name;
247: public string TaskName
248: {
249: get
250: {
251: return _name;
252: }
253: set
254: {
255: if (_name != value)
256: {
257: _name = value;
258: this.NotifyPropertyChanged("TaskName");
259: }
260: }
261: }
262: #endregion TaskName
263:
264: #region ResourceName
265: private string _resourceName;
266: public string ResourceName
267: {
268: get
269: {
270: return _resourceName;
271: }
272: set
273: {
274: if (_resourceName != value)
275: {
276: _resourceName = value;
277: this.NotifyPropertyChanged("ResourceName");
278: }
279: }
280: }
281: #endregion ResourceName
282:
283: }
Implement a ViewModel to present the whole project and the selected task in a Gantt control
1: class ProjectViewModel : ObservableModel
2: {
3: #region Constructor
4: public ProjectViewModel()
5: {
6: this.Tasks = new ObservableCollection();
7: }
8: #endregion Constructor
9:
10: #region SelectedTask
11: private TaskModel _selectedTask;
12: public TaskModel SelectedTask
13: {
14: get
15: {
16: return _selectedTask;
17: }
18: set
19: {
20: if (value != null)
21: {
22: _selectedTask = value;
23: }
24:
25: NotifyPropertyChanged("SelectedTask");
26: }
27: }
28: #endregion SelectedTask
29:
30: #region Tasks
31: private ObservableCollection _tasks;
32: public ObservableCollection Tasks
33: {
34: get
35: {
36: return _tasks;
37: }
38: set
39: {
40: if (value != null)
41: {
42: _tasks = value;
43: }
44:
45: NotifyPropertyChanged("Tasks");
46: }
47: }
48: #endregion //Tasks
49:
50: }
Create a ListBackedProject and bind it to data
1: <ig:ListBackedProject x:Name="dataProvider" TaskItemsSource="{Binding Tasks}">
2: <ig:ListBackedProject.TaskPropertyMappings>
3: <ig:ProjectTaskPropertyMappingCollection UseDefaultMappings="True">
4:
5:
6:
7:
8: ig:ProjectTaskPropertyMappingCollection>
9: ig:ListBackedProject.TaskPropertyMappings>
10: <ig:ListBackedProject.TaskSettings>
11: <ig:ProjectTaskSettings AllowChangeIndentation="True"
12: AllowDelete="True"
13: AllowDragDeadline="True"
14: AllowDragMilestone="True"
15: AllowDragPercentComplete="True"
16: AllowDragSummary="Always"
17: AllowDragTask="True"
18: AllowInsert="True"
19: AllowResizeSummary="Always"
20: AllowResizeTask="True"/>
21: ig:ListBackedProject.TaskSettings>
22:
23: ig:ListBackedProject>
Set XamGantt Column Settings (optional)
These settings are used to define how to maintain task settings in Gantt control
1: <ig:XamGantt Margin="10" x:Name="xamGantt" VerticalAlignment="Top" ActiveCellChanged="xamGantt_ActiveCellChanged" Project="{Binding ElementName=dataProvider}">
2: <ig:XamGantt.DefaultColumnSettings>
3: <ig:ProjectColumnSettings AllowHide="True"
4: AllowMove="True"
5: AllowResize="True"
6: AllowShow="True" />
7: ig:XamGantt.DefaultColumnSettings>
8: ig:XamGantt>
Create a mappings collection using TaskPropertyMappings
In every ProjectTaskPropertyMapping, the TaskProperty specifies a ProjectTask property and the DataObjectProperty specifies the corresponding custom task property.
Mandatory Mappings:
The following mappings are mandatory:
- DataItemId – to uniquely identify the task
- Tasks – to store the child tasks of a task
- ConstraintType/ConstraintDate
- DurationFormat – to specify the units for the duration and to track whether the duration is an elapsed duration.
1:
2: <ig:ProjectTaskPropertyMapping TaskProperty="DataItemId"
3: DataObjectProperty="TaskID" />
4: <ig:ProjectTaskPropertyMapping TaskProperty="ConstraintType"
5: DataObjectProperty="ConstraintType" />
8: <ig:ProjectTaskPropertyMapping TaskProperty="DurationFormat"
9: DataObjectProperty="DurationFormat" />
10: <ig:ProjectTaskPropertyMapping TaskProperty="Tasks"
11: DataObjectProperty="Tasks" />
12:
Mandatory mappings are required to have an own project ViewModel with custom task. These mapping don’t allow to maintain tasks activation and to schedule manually tasks.
Schedule manually tasks in the project.
To support manual tasks scheduling you need to map property from your custom task model to IsManual property from ProjectTask class
1:
2:
3: <ig:ProjectTaskPropertyMapping TaskProperty="IsManual"
4: DataObjectProperty="IsUndetermined" />
5:
6:
In this case you can change the state of the task schedule between manual and automatic.
Maintain task active/inactive state
You need to map ProjectTask.IsActive property to a property from your custom task model
1:
2:
3: <ig:ProjectTaskPropertyMapping TaskProperty="IsActive"
4: DataObjectProperty="IsInProgress" />
5:
6:
In this case you can change the state of the task schedule between manual and automatic.
Other task property mappings.
The sample below shows other property mappings. If you need to change some task properties via ViewModel you need to implement an appropriate mapping.
1: <ig:ListBackedProject.TaskPropertyMappings>
2: <ig:ProjectTaskPropertyMappingCollection UseDefaultMappings="True">
3:
4:
5: <ig:ProjectTaskPropertyMapping TaskProperty="DataItemId"
6: DataObjectProperty="TaskID" />
7: <ig:ProjectTaskPropertyMapping TaskProperty="ConstraintType"
8: DataObjectProperty="ConstraintType" />
9: <ig:ProjectTaskPropertyMapping TaskProperty="ConstraintDate"
10: DataObjectProperty="ConstraintDate" />
11: <ig:ProjectTaskPropertyMapping TaskProperty="DurationFormat"
12: DataObjectProperty="DurationFormat" />
13: <ig:ProjectTaskPropertyMapping TaskProperty="Tasks"
14: DataObjectProperty="Tasks" />
15:
16:
17:
18:
19: <ig:ProjectTaskPropertyMapping TaskProperty="IsManual"
20: DataObjectProperty="IsUndetermined" />
21:
22:
23:
24:
25:
26: <ig:ProjectTaskPropertyMapping TaskProperty="Predecessors" DataObjectProperty="Predecessors" />
27:
28:
29:
30:
31:
32: <ig:ProjectTaskPropertyMapping TaskProperty="IsActive"
33: DataObjectProperty="IsInProgress" />
34:
35:
36:
37:
38:
39: <ig:ProjectTaskPropertyMapping TaskProperty="TaskName"
40: DataObjectProperty="TaskName" />
41:
42: <ig:ProjectTaskPropertyMapping TaskProperty="Start"
43: DataObjectProperty="Start" />
44:
45:
46: <ig:ProjectTaskPropertyMapping TaskProperty="Duration"
47: DataObjectProperty="Duration" />
48:
49:
50: <ig:ProjectTaskPropertyMapping TaskProperty="IsMilestone"
51: DataObjectProperty="IsMilestone" />
52:
53: <ig:ProjectTaskPropertyMapping TaskProperty="Deadline"
54: DataObjectProperty="DeadlineDate" />
55:
56:
57:
58:
59: ig:ProjectTaskPropertyMappingCollection>
60: ig:ListBackedProject.TaskPropertyMappings>
Binding to Data Using Project
If you want to load a data from external source like a Microsoft Project 2010 XML file or you want to create an empty project and fill the tasks later, you may use the XamGantt Project property. In this scenario you can maintain data using XamGantt built-in classes and import/export projects using other formats.
To be possible to use built-in project tasks you need to:
- Create an instance of the XamGantt
- Create an instance of Project
- Set the XamGantt Project property to the created Project instance
Create a ViewModel with a property representing a Gantt Project
1: class NativeProjectViewModel : ObservableModel
2: {
3: #region Constructor
4: public NativeProjectViewModel()
5: {
6: this._project = new Project();
7: ProjectTask root = new ProjectTask { TaskName = "Root Task", IsManual = false, Duration = new TimeSpan(3, 0, 0, 0) };
8:
9: Project.RootTask.Tasks.Add(root);
10: }
11: #endregion //Constructor
12:
13: #region SelectedTask
14: private ProjectTask _selectedTask;
15: public ProjectTask SelectedTask
16: {
17: get
18: {
19: return _selectedTask;
20: }
21: set
22: {
23: if (value != null)
24: {
25: _selectedTask = value;
26: }
27:
28: NotifyPropertyChanged("SelectedTask");
29: }
30: }
31: #endregion SelectedTask
32:
33: #region Project
34: private Project _project;
35: public Project Project
36: {
37: get
38: {
39: return this._project;
40: }
41: set
42: {
43: if (this._project != value)
44: {
45: this._project = value;
46: this.NotifyPropertyChanged("Project");
47: }
48: }
49: }
50: #endregion //Project
51:
52: }
Create an instance of Project
1:
2: "nativeviewmodel" />
3:
Set the XamGantt Project property to the created Project instance
1: "{StaticResource nativeviewmodel}">
2: "10" x:Name="nativeGantt" ActiveCellChanged="nativeGantt_ActiveCellChanged" Project="{Binding Project}"/>
3:
Create Task
You can create a task programmatically or via XamGantt UI (Context menu)
Create Tasks (Using ListBackedProject, Arbitrary Tasks Collection and Custom Tasks)
The sample below demonstrates how to create a custom task and add it to Tasks collection of your ViewModel (using C#)
1: TaskModel newTask = new TaskModel();
2:
3: newTask.DurationFormat = Infragistics.Controls.Schedules.ProjectDurationFormat.ElapsedDays;
4: newTask.Duration = new TimeSpan(8, 0, 0);
5: newTask.IsMilestone = false;
6: newTask.TaskName = "CustomTask";
7: newTask.Start = DateTime.Now;
8: newTask.IsUndetermined = true;
9:
10: model.Tasks.Add(newTask);
11: model.SelectedTask = newTask;
Create Tasks (Using Project and ProjectTask)
The sample below demonstrates how to create a ProjectTask instance task and add it to RootTask.Tasks collection of your the Project instance (using C#)
1: ProjectTask newTask = new ProjectTask();
2:
3: newTask.DurationFormat = Infragistics.Controls.Schedules.ProjectDurationFormat.ElapsedDays;
4: newTask.Duration = new TimeSpan(8, 0, 0);
5: newTask.IsMilestone = false;
6: IEnumerable existingTasks = null;
7: newTask.TaskName = "MyTask";
8: newTask.Start = DateTime.Now;
9:
10: model.Project.RootTask.Tasks.Add(newTask);
11: model.SelectedTask = newTask;
Create a task programmatically or via XamGantt UI (Context menu –> Insert Task)
Maintain Tasks Links (Tasks Dependencies)
The XamGantt control provides functionality for creating dependency links between tasks within a project and supports the link types available in Microsoft Project 2010. You can specify a lead and lag time of the dependency as well.
You create tasks dependencies programmatically using collections of tasks’ predecessors and successors. Use the ProjectTask Predecessors and Successors properties to create a collection of ProjectTaskDependency objects.
- The Successors and Predecessors collections expose two overload methods of the Add method.
- The first one takes as parameters ProjectTask and ProjectTaskLinkType.
- The second one takes as parameters ProjectTask, ProjectTaskLinkType and ProjectDuration.
Link type notation |
Link type |
Description |
FS |
Finish To Start |
If not otherwise specified this is the default link type. The successor's Start date is dependent upon the predecessor's Finish date. |
FF |
Finish To Finish |
The successor's Finish date is dependent upon the predecessor's Finish date. |
SF |
Start To Finish |
The successor's Finish date is dependent upon the predecessor's Start date. |
SS |
Start To Start |
The successor's Start date is dependent upon the predecessor's Start date. |
Create Links (Using ListBackedProject, Arbitrary Tasks Collection and Custom Tasks)
To be possible to maintain task links using custom tasks you need to have at least one of these properties (Predecessors and Successors) or to have an appropriate custom properties that you can map to ProjectTask.Predecessors and/or ProjectTask.Successors.
1: #region Predecessors
2: private string _predecesors;
3: public string Predecessors
4: {
5: get { return _predecesors; }
6: set
7: {
8: if (value != _predecesors)
9: {
10: _predecesors = value;
11: this.NotifyPropertyChanged("Predecessors");
12: }
13: }
14: }
15: #endregion //Predecessors
16:
17: #region Successors
18: private string _successors;
19: public string Successors
20: {
21: get { return _successors; }
22: set
23: {
24: if (value != _successors)
25: {
26: _successors = value;
27: this.NotifyPropertyChanged("Successors");
28: }
29: }
30: }
31: #endregion //Successors
You can maintain tasks dependencies also from XamGantt UI
Create Links (Using Project and ProjectTask)
Build-in ProjectTask class contains own Predecessors and Successors properties. You can use these properties in this case instead mapped properties of custom tasks.
1: ProjectTask task = new ProjectTask();
2: task.DurationFormat = Infragistics.Controls.Schedules.ProjectDurationFormat.ElapsedDays;
3: task.ConstraintDate = new DateTime(2012, 10, 10, 8, 0, 0);
4: task.ConstraintType = ProjectTaskConstraintType.FinishNoLaterThan;
5: task.Duration = new TimeSpan(80, 0, 0);
6:
7: task.IsMilestone = false;
8: task.TaskName = "Sample task";
9: task.TaskName = "Sample task"
10: task.Start = new DateTime(2012, 10, 2);
11:
12: model.Project.RootTask.Tasks.Add(task);
13:
14: ProjectTask lastTask = this.nativeGantt.Project.RootTask.Tasks.Last();
15:
16: if (!task.Equals(lastTask))
17: {
18: lastTask.Predecessors.Add(task, ProjectTaskLinkType.FinishToStart, ProjectDuration.FromFormatUnits(-2, ProjectDurationFormat.Days));
19:
20: }
Edit Task
To edit tasks you can use Infragistics Gantt UI or to change tasks properties with code.
Delete Task
You can create a task programmatically with code or via XamGantt UI (Context menu)
Delete Custom Task
To delete custom tasks it is enough to remove the task from Arbitrary Tasks Collection
1: var model = this.Root.Resources["viewmodel"] as ProjectViewModel;
2: if (model != null)
3: {
4: DeleteCustomTask(model);
5:
6: }
7:
8: //Delete custom task
9:
10: private void DeleteCustomTask(ProjectViewModel model)
11: {
12: if (model.SelectedTask != null)
13: {
14: model.Tasks.Remove(model.SelectedTask);
15: model.SelectedTask = null;
16: }
17: }
Delete Project Task
To delete a built-in ProjectTask instance you need to remove the task from Project.RootTask.Tasks collection
1: var model = this.Root.Resources["nativeviewmodel"] as NativeProjectViewModel;
2: if (model != null)
3: {
4: DeleteNativeTask(model);
5:
6: }
7:
8: //Delete project task
9:
10: private void DeleteNativeTask(NativeProjectViewModel model)
11: {
12: if (model.SelectedTask != null && model.SelectedTask != model.Project.RootTask)
13: {
14: model.Project.RootTask.Tasks.Remove(model.SelectedTask);
15: model.SelectedTask = null;
16: }
17: }
You can look at the sample application to see how to delete task in both cases
Delete Task from XamGantt UI (Context menu –> Delete Task)
Infragistics Gantt control is outright AWESOME! You can have almost everything from Microsoft Project, getting rid of all the useless stuff, and cramming it into a beautiful, lightweight, and high performance control that can be dragged into your WPF and Silverlight applications. Expect soon a new amazing Infragistics NetAdvantage Vol.12.2.
Follow news from Infragistics for more information about new Infragistics products.
Source code is available here. Sample project is created with Visual Studio 2012, but you can use NetAdvantage 12.2 controls with both: Visual Studio 2012 and Visual Studio 2012. As always, you can follow us on Twitter @mihailmateev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!