[Canonical page](<https://docs.dhtmlx.com/planning-engine/net/api/project-preview/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Project.Preview

Applies hypothetical changes to a copy of the solved baseline of a [`Project`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/project.md>) and schedules the copy. The project isn't modified. Use it to show what an edit would do before committing it with [`Apply`](<https://docs.dhtmlx.com/planning-engine/net/api/project-apply.md>).

## Signature

```csharp
public ScheduleResult Preview(SchedulingChanges changes, PreviewOptions options)
```

Synchronous and CPU-only. Reads the project without changing it. [`Project`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/project.md>) isn't thread-safe, so don't call `Preview` on one instance from several threads at once.

## Parameters

| Name | Type | Description |
|---|---|---|
| `changes` | [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/scheduling-changes.md>) | The edits to try |
| `options` | [`PreviewOptions`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/preview-options.md>) | Subtree, analytics, reasons, baseline check and an optional load profile |

## Behavior

- [`Updates`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/schedule-result.md#updates>) are relative to the baseline. An empty change set gives empty `Updates`.
- The result's [`BaselineId`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/schedule-result.md#baselineid>) is the baseline the preview read, so a following `Preview` accepts it.
- The change set is all or nothing: one rejected operation fails the whole call.
- `options.BaselineId` and `changes.BaselineId` are checked separately. `null` skips a check.
- An explicit start on non-working time is kept unsnapped when the task has no predecessor. The end is computed forward from it on the working calendar.

## Returns

A [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/schedule-result.md>) for the previewed state.

## Errors

| Error | When |
|---|---|
| [`ProjectException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/project-exception.md>) with `Code == ProjectError.NoBaseline` | The project is unsolved, including after `Apply` |
| `ProjectException` with `Code == ProjectError.StaleBaseline` | `options.BaselineId` or `changes.BaselineId` doesn't name the current baseline |
| [`InputException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/input-exception.md>) | An operation in `changes` is rejected (`Path` and `Message` describe the first one), or the [`Profile`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/preview-options.md#profile>) query is invalid (`To <= From` or too many buckets at `to`, an unknown resource at `resources`) |
| `ArgumentNullException` | `changes` or `options` is `null` |

## Example

Preview a drag of task `b`, then check that the project keeps its dates.

```csharp
using Plen;

static Moment Day(int day) =>
    Moment.FromYmdHm(2026, 1, day, 0, 0) ?? throw new InvalidOperationException("invalid date");

var data = new ProjectData
{
    Tasks = [new TaskData("b") { StartDate = Day(1), Duration = 1.0 }],
};
data.Config.UseCalendars = false;

var project = new Project();
var solved = project.Solve(data, new SolveOptions());

var drag = new SchedulingChanges();
drag.Tasks.Update.Add(new TaskUpdate("b") { StartDate = Day(5) });

var preview = project.Preview(drag, new PreviewOptions { BaselineId = solved.BaselineId });
foreach (var update in preview.Updates)
    Console.WriteLine($"task {update.Id}: {update.StartDate} .. {update.EndDate}");
// task b: 2026-01-05 00:00 .. 2026-01-06 00:00

// The project is unchanged: an empty preview reports no updates.
var unchanged = project.Preview(new SchedulingChanges(), new PreviewOptions());
Console.WriteLine(unchanged.Updates.Count); // 0
```

## Related

- [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/scheduling-changes.md>) - the edits to try
- [`PreviewOptions`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/preview-options.md>) - the options
- [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/schedule-result.md>) - the returned result
- [`Project.Apply`](<https://docs.dhtmlx.com/planning-engine/net/api/project-apply.md>) - commits the edits once accepted
- [Previewing changes](<https://docs.dhtmlx.com/planning-engine/net/live-projects/previewing-changes.md>) - trying edits before committing them
