Table of Contents

Tasks & hierarchy

Tasks are the heart of a project. Each TTMSFNCGanttTask carries a name and description, a planned start and duration, a calculated schedule, a progress percentage, an optional cost and state, and a collection of subtasks that form a work-breakdown hierarchy. This chapter covers adding tasks at the project root and as nested subtasks, the sibling and event-marker helpers, reading the calculated schedule, and finding, moving, locking, and deleting tasks. Reach for the hierarchy whenever a plan has phases that contain smaller pieces of work.

Adding tasks and subtasks

Project.AddTask appends a root task; Task.AddSubTask nests a task below another. Both take the name, description, planned start, and a duration value plus its unit (gdtSeconds, gdtMinutes, gdtHours, gdtWorkDays), and return the created task. A planned start of 0 (or Now) lets the task calculate its own start from dependencies and the calendar. AddNextEventMarker adds a zero-duration milestone:

procedure TForm1.BuildTaskTree;
var
  Phase, Sub: TTMSFNCGanttTask;
begin
  GanttProject1.BeginUpdate;
  try
    GanttProject1.ClearTasks;

    // Root task.
    Phase := GanttProject1.AddTask('Phase 1 — Analysis', 'Requirements and planning',
      Now, 4, gdtWorkDays);
    Phase.Progress := 100;

    // Nested subtasks below the root task.
    Sub := Phase.AddSubTask('Interviews', 'Stakeholder interviews',
      Now, 2, gdtWorkDays, whWorkTimeAndDays, 100);
    Sub.Cost := 1500;

    Sub := Phase.AddSubTask('Specification', 'Write the spec',
      Now + 2, 2, gdtWorkDays, whWorkTimeAndDays, 60);
    Sub.Locked := True;   // prevent interactive reschedule

    // A zero-duration milestone marker after the phase.
    Phase.AddNextEventMarker('Sign-off', 'Analysis approved');
  finally
    GanttProject1.EndUpdate;
  end;
end;

procedure TForm1.FindAndMoveTask;
var
  Task: TTMSFNCGanttTask;
begin
  Task := GanttProject1.GetTaskByTaskName('Specification');
  if Assigned(Task) then
    Task.MoveTaskTime(1);   // shift one day later
end;
A chart showing a parent phase with indented subtasks and a milestone marker A chart showing a parent phase with indented subtasks and a milestone marker
Note

Wrap task building in BeginUpdate/EndUpdate. Scheduling (CalculateTasks) runs once on EndUpdate instead of after every change.

Task properties

Property Description
Name Display name of the task
Description Longer description
PlannedStart Requested start date/time before dependency scheduling
PlannedDuration Planned duration (Value + DurationType)
PlannedEnd Planned finish date/time
ScheduledStart / ScheduledEnd Calculated dates after dependencies and the calendar are applied (read-only)
Progress Completion percentage
Cost Cost value; a parent can reflect the sum of its subtasks
WorkTimePolicy Calendar policy used to schedule the task (see Work-time calendar)
TaskStateName Name of the assigned task state
IsEventMarker Renders the task as a zero-duration milestone
Locked Blocks interactive and programmatic schedule changes
WBS Work-breakdown path computed from the hierarchy (read-only)
TaskId / DBKey Application identifier / data-binding key
Note

PlannedStart is what you request; ScheduledStart is what the project calculates after applying dependencies and the work-time calendar. Read ScheduledStart/ScheduledEnd to position or report a task — they can differ from the planned values.

Sibling and dependent helpers

Besides AddSubTask, a task exposes sibling helpers that place a new task relative to itself — AddNextTask, AddNextTaskSameStartDate, AddNextTaskSameEndDate — and AddDependentTask, which creates a task and its dependency in one call (see Dependencies). Event-marker variants (AddSubEventMarker, AddNextEventMarker, AddDependentEventMarker) add zero-duration milestones.

Finding, moving, and deleting tasks

The project offers lookups by name, identifier, WBS, and flattened index: GetTaskByTaskName, GetTaskByTaskId, GetTaskByWBS, GetTaskByCount, plus GetNextTask/GetPreviousTask for ordered traversal. MoveTaskTime shifts a task's schedule by a time offset, and DeleteTask / DeleteTaskByTaskId remove tasks. The snippet above finds a task by name and moves it one day later.

Task states

A project can carry a set of named States (priority + appearance) that tasks reference by name. Call Project.SetDefaultStates to create the built-in set, then assign a task's state with Task.SetTaskStateByName('...') or Task.TaskStateName. States let many tasks share one appearance and priority definition managed in a single place.

Per-task bar appearance

Most styling is the chart's job, but a single task can override its own bar look through Task.TaskAppearance (TTMSFNCGanttTaskAppearance): Fill and Stroke for the bar, ProgressFill / ProgressStroke for the completed portion, plus Corners, Rounding, and Font. Use it to flag one task — a critical milestone or an at-risk item — without defining a shared task state:

{ Inside a method of your form, with a task reference already obtained: }
Task.TaskAppearance.Fill.Color := gcCrimson;
Task.TaskAppearance.ProgressFill.Color := gcDarkred;
Task.TaskAppearance.Rounding := 4;

For styling that applies to every task, prefer a shared task state or the chart's appearance options instead of per-task overrides.

Putting it together

A typical flow combines several of the operations above: build a hierarchy, then locate a task by name and read its calculated schedule back:

procedure TForm1.BuildAndInspect;
var
  Phase, Spec: TTMSFNCGanttTask;
begin
  GanttProject1.BeginUpdate;
  try
    GanttProject1.ClearTasks;
    // Adding + hierarchy
    Phase := GanttProject1.AddTask('Analysis', 'Requirements', Now, 4, gdtWorkDays);
    Phase.AddSubTask('Interviews', '', Now, 2, gdtWorkDays, whWorkTimeAndDays, 100);
    Phase.AddSubTask('Specification', '', Now + 2, 2, gdtWorkDays);
  finally
    GanttProject1.EndUpdate;
  end;

  // Finding + reading the calculated schedule
  Spec := GanttProject1.GetTaskByTaskName('Specification');
  if Assigned(Spec) then
    Caption := Format('Specification: %s - %s',
      [DateToStr(Spec.ScheduledStart), DateToStr(Spec.ScheduledEnd)]);
end;

Pitfalls

  • Setting PlannedStart does not guarantee ScheduledStart matches it — a dependency or the work-time calendar can push the calculated start out.
  • A Locked task ignores reschedule attempts; clear Locked before moving it programmatically.
  • Build tasks inside BeginUpdate/EndUpdate; without it, every AddTask triggers a full recalculation.

See also