Project Management
A Gantt chart: the plan as a task list beside a timeline of bars, with the links between the tasks, the milestones, the baselines and the critical path, scheduled on a working calendar and editable with the mouse or the keyboard alone.
Assembly
| Assembly | Binarymission.WPF.Controls.ProjectManagement.dll |
| Root namespace | Binarymission.WPF.Controls.ProjectManagement |
| Target frameworks | .NET Framework 4.8, .NET 6 / 8 / 10 |
Overview
Use it wherever work is planned against dates – a product launch, a fit-out, a release – to answer what a planner asks of a plan (when does it finish, what is late, which tasks have no slack) and to let them change it by dragging.
The plan
A plan is two lists that belong to the application: Tasks, a flat list of
ProjectTask objects in which a subtask names its summary through
ProjectTask.ParentId, and Links, the TaskLink dependencies
between them – the shape a database table or a spreadsheet already has. An
ObservableCollection is watched, and so is every task and link in it: change a
property and the chart schedules again and redraws.
Scheduling
Dates are worked out by the critical-path method on working days – the days
WorkingWeek names, less the Holidays, from ProjectStart. A
task starts on its planned ProjectTask.Start or as soon as its links allow, whichever
is later, and a summary spans its subtasks. The result is in Schedule, and
ScheduleChanged says when it has been worked out again. Bad data never stops the
chart: a link to a missing task, or a loop, is left out and reported in
ProjectSchedule.Problems, and everything else is drawn.
The two panes
The task list on the left shows the outline and the Columns you choose; the
timeline on the right shows a bar for each task, a diamond for each milestone and a bracket for each
summary, at the scale Zoom sets. The panes scroll up and down together; the splitter
between them sets GridWidth.
Editing
Drag a bar to move the task, its right-hand end to change its length, or the triangle under it
to set its progress; drag from the dot past its end to another row to link the two finish-to-start.
Press F2 to type into the current cell – name, start, days, % done or predecessors (outline numbers
such as 2, 3SS+1). Every edit is offered first through TaskChanging or
LinksChanging, which can cancel it, then made to the task or link object itself and
announced by TaskChanged or LinksChanged. IsReadOnly
allows looking only.
The keyboard
| Keys | What they do |
|---|---|
Up, Down, Home, End, Page Up, Page Down | Move between rows. |
Right, Left | Open or close a summary, or go to its first subtask or back; in other columns, move between cells. |
F2; Enter or Space | Edit the current cell; raise
|
Shift+Left, Shift+Right | Move the task a working day earlier or later. |
Ctrl+Shift+Left, Ctrl+Shift+Right | Make it a working day shorter or longer. |
Ctrl+Plus, Ctrl+Minus, Ctrl+0 | Zoom in, out, or to fit the plan. Esc cancels a drag. |
One plan, three views
Give the same lists to a PertChart and a TaskBoard and the three
agree: they schedule the same objects with the same arithmetic, so a bar dragged here or a card
moved on the board is seen by all of them.
Appearance
The chart draws itself and has no template. Control.Background,
Control.Foreground and the font apply as usual; the bars, links and calendar have
brush properties (BarBrush, CriticalBrush and the rest) whose defaults
read on light and dark backgrounds alike, and in Windows high contrast the system colours replace
them all. The chart always runs left to right, as a calendar does. ExportImage and
Print draw the whole plan.
Getting started
A window that plans a website relaunch, built in code so that it stands on its own; in XAML
the lists are bound, as in <pm:GanttChart Tasks="{Binding Tasks}" Links="{Binding Links}"
Zoom="Week" /> with
xmlns:pm="clr-namespace:Binarymission.WPF.Controls.ProjectManagement;assembly=Binarymission.WPF.Controls.ProjectManagement".
using System;
using System.Collections.ObjectModel;
using System.Windows;
using Binarymission.WPF.Controls.ProjectManagement;
public sealed class PlanWindow : Window
{
public PlanWindow()
{
var tasks = new ObservableCollection<ProjectTask>
{
new ProjectTask("design", "Design"), // a summary: it has subtasks
new ProjectTask("wire", "Wireframes", 5) { ParentId = "design", Assignee = "Priya Shah", Progress = 100 },
new ProjectTask("look", "Visual design", 8) { ParentId = "design", Assignee = "Tom Ellis", Progress = 40 },
new ProjectTask("build", "Build"),
new ProjectTask("web", "Front end", 10) { ParentId = "build", Assignee = "Ana Ruiz" },
new ProjectTask("api", "Orders API", 12) { ParentId = "build", Assignee = "Sam Okafor" },
new ProjectTask("live", "Go live", 0), // no length: a milestone
};
var links = new ObservableCollection<TaskLink>
{
new TaskLink("wire", "look"), // finish-to-start
new TaskLink("look", "web", TaskLinkType.StartToStart, 3), // 3 working days after it starts
new TaskLink("wire", "api"),
new TaskLink("web", "live"),
new TaskLink("api", "live"),
};
var gantt = new GanttChart
{
Tasks = tasks,
Links = links,
ProjectStart = new DateTime(2026, 11, 2),
Columns = GanttColumns.Default | GanttColumns.Assignee,
};
gantt.Holidays.Add(new DateTime(2026, 12, 25));
// A finished task stays where it is, whichever way the reader tries to move it.
gantt.TaskChanging += (sender, e) =>
e.Cancel = e.Task.Progress >= 100 && (e.NewStart.HasValue || e.NewDuration.HasValue);
gantt.ScheduleChanged += (sender, e) =>
Title = "Website relaunch - finishes " + gantt.Schedule.ProjectFinish.ToString("d MMM yyyy");
Content = gantt;
Loaded += (sender, e) => gantt.ZoomToFit();
}
}API reference
Binarymission.WPF.Controls.ProjectManagement
class BoardColumn | A column of the task board: one value of Status, the heading shown for it, and the most cards it should hold. |
enum BoardLaneBy | What the task board splits its rows - its lanes - by: nothing, the assignee, the category, or the phase (top-level summary task) each piece of work belongs to. |
class CardMovingEventArgs | Provides data for the task board's CardMoving event: a card about to move to another column, lane or position, which a handler can inspect and refuse. |
class GanttChart | A Gantt chart: the plan as a task list beside a timeline of bars, with the links between the tasks, the milestones, the baselines and the critical path, scheduled on a working calendar and editable with the mouse or the keyboard alone. |
enum GanttColumns | The columns of the Gantt chart's task list, as flags that combine. The task's name is always shown. |
enum GanttZoom | How much time the Gantt chart's timeline shows, from a column per day to a column per half-year. |
class LinksChangingEventArgs | Provides data for the Gantt chart's LinksChanging event: links the reader is about to add to the plan or remove from it, all leading into one task. |
class PertChart | A PERT chart: the plan as a network of boxes, one for each piece of work, joined by arrows in the order the work has to happen, each box showing the dates and float the critical-path method gives its task. |
class ProjectSchedule | A plan as scheduled: every task's dates and float, the links that were honoured, the critical path, the project's start and finish, and anything in the input that could not be used. |
class ProjectScheduler | Schedules a project plan by the critical-path method: works out every task's earliest and latest dates on a working calendar, its float, and the chain of tasks that decides when the plan finishes. |
class ProjectTask | One row of a project plan - a piece of work, a summary that groups other tasks, or a milestone - as the Gantt chart, the PERT chart and the task board all read it. |
class ScheduleInput | Everything Schedule(ScheduleInput) needs to schedule a plan: the tasks, the links between them, the project's first day and the working calendar. |
class ScheduleProblem | Something in a plan that could not be used as written: a duplicate id, a link to a missing task, a loop of links. The scheduler never throws for bad data; it reports it here, leaves it out, and schedules everything else. |
enum ScheduleProblemKind | What kind of problem the scheduler found in a plan. Each kind says what was wrong and what was done about it, so that the rest of the plan could still be scheduled. |
class ScheduledLink | One link as the scheduler used it. Links that could not be honoured are not here; they are in Problems instead. |
class ScheduledTask | One task as scheduled: the dates the links allow, how much it can slip, whether it is critical, and where it sits in the outline. |
class TaskBoard | A task board: the plan as cards in columns - one column for each stage of the work, one card for each task - with optional lanes across the board by assignee, category or phase, and cards that move with the mouse or the keyboard. |
class TaskChangingEventArgs | Provides data for the Gantt chart's TaskChanging event: a change the reader is about to make to one task, which a handler can inspect and refuse. |
enum TaskConstraintKind | How a task's own planned date holds it, in addition to its links: as the earliest it may start, as a date it must start on, or as the earliest it may finish. |
enum TaskEditSource | How a change to the plan was made: with the pointer, the keyboard, by typing in a cell, or by the application. |
class TaskEventArgs | Provides data for the events that concern one task: a task changed, activated, opened or closed, or a card moved. |
class TaskLink | A dependency between two tasks: the successor (ToId) waits for the predecessor (FromId) in the way Type says, give or take Lag working days. |
enum TaskLinkType | Which end of the predecessor a link waits for, and which end of the successor it holds: the four dependency types of the critical-path method. |
class WorkCalendar | A working calendar: which days of the week are worked and which dates are not, and the conversion between calendar dates and the count of working days the scheduler plans in. |
enum WorkingWeek | The days of the week that are worked, as flags that combine, so a whole working week is one property value: WorkingWeek.Monday | WorkingWeek.Tuesday | WorkingWeek.Wednesday | WorkingWeek.Thursday for a four-day week. |