UndoRedoManager Class

Records actions and provides asynchronous undo/redo capabilities. Actions can be grouped into transactions and consecutive actions can be merged into a single undo step.

public sealed class UndoRedoManager : INotifyPropertyChanged

Remarks

This type is not thread-safe; all operations are expected to run on a single logical flow.

Example

var manager = new UndoRedoManager();
var list = new List<int>();

// Asynchronous execute and unexecute delegates
await manager.RecordActionAsync(
    execute: ct => { list.Add(1); return ValueTask.CompletedTask; },
    unexecute: ct => { list.RemoveAt(list.Count - 1); return ValueTask.CompletedTask; });

// Synchronous execute and unexecute delegates
await manager.RecordActionAsync(
    execute: () => list.Add(2),
    unexecute: () => list.RemoveAt(list.Count - 1));
Assert.Equal([1, 2], list);

await manager.UndoAsync();
Assert.Equal([1], list);

await manager.RedoAsync();
Assert.Equal([1, 2], list);
Implements

Constructors

NameDescription
UndoRedoManager()Initializes a new instance of the UndoRedoManager class with an unbounded history.
UndoRedoManager(int)Initializes a new instance of the UndoRedoManager class keeping at most maxHistoryDepth undo steps.

UndoRedoManager overloads

UndoRedoManager()

UndoRedoManager.cs

Initializes a new instance of the UndoRedoManager class with an unbounded history.

public UndoRedoManager()

UndoRedoManager(int)

UndoRedoManager.cs

Initializes a new instance of the UndoRedoManager class keeping at most maxHistoryDepth undo steps.

public UndoRedoManager(int maxHistoryDepth)
Parameters
maxHistoryDepth int
The maximum number of undo steps to keep. Once exceeded, the oldest steps are dropped and can no longer be undone.
Exceptions
ArgumentOutOfRangeException
maxHistoryDepth is not strictly positive.

Properties

NameDescription
ActionIsExecutingGets a value indicating whether an action is currently being executed, undone, or redone.
CanRedoGets a value indicating whether UndoRedoManager.RedoAsync would re-execute an action right now.
CanUndoGets a value indicating whether UndoRedoManager.UndoAsync would revert an action right now.
MaxHistoryDepthGets the maximum number of undo steps kept in the history.
RedoableActionsGets a snapshot of the actions that can be redone, most recent first.
TransactionDepthGets the number of transactions that are currently open.
UndoableActionsGets a snapshot of the actions that can be undone, most recent first.

ActionIsExecuting

UndoRedoManager.cs

Gets a value indicating whether an action is currently being executed, undone, or redone.

public bool ActionIsExecuting { get; }

Property value

bool

Remarks

While this is true, any operation that changes the history throws an InvalidOperationException. An action must not record, undo, or redo anything itself.

Gets a value indicating whether UndoRedoManager.RedoAsync would re-execute an action right now.

public bool CanRedo { get; }

Property value

bool

Remarks

This is false while a transaction is open or an action is executing, so it can be bound directly to a command's availability.

Gets a value indicating whether UndoRedoManager.UndoAsync would revert an action right now.

public bool CanUndo { get; }

Property value

bool

Remarks

This is false while a transaction is open or an action is executing, so it can be bound directly to a command's availability.

MaxHistoryDepth

UndoRedoManager.cs

Gets the maximum number of undo steps kept in the history.

public int MaxHistoryDepth { get; }

Property value

int

RedoableActions

UndoRedoManager.cs

Gets a snapshot of the actions that can be redone, most recent first.

public IReadOnlyList<IUndoRedoAction> RedoableActions { get; }

Property value

IReadOnlyList<IUndoRedoAction>

Remarks

The snapshot is rebuilt when the history changes rather than on every access, so an instance obtained earlier stays unchanged.

TransactionDepth

UndoRedoManager.cs

Gets the number of transactions that are currently open.

public int TransactionDepth { get; }

Property value

int

Remarks

Undo, redo and UndoRedoManager.Clear are unavailable while this is greater than zero.

UndoableActions

UndoRedoManager.cs

Gets a snapshot of the actions that can be undone, most recent first.

public IReadOnlyList<IUndoRedoAction> UndoableActions { get; }

Property value

IReadOnlyList<IUndoRedoAction>

Remarks

The snapshot is rebuilt when the history changes rather than on every access, so an instance obtained earlier stays unchanged.

Methods

NameDescription
Clear()Empties both the undo and redo buffers.
CreateTransaction(TransactionExecutionMode)Begins a transaction that groups subsequent recorded actions into a single undo step.
RecordActionAsync(IUndoRedoAction, CancellationToken)Executes an action and records it so it can later be undone.
RecordActionAsync(Action, Action, CancellationToken)Executes a synchronous execute delegate and a synchronous unexecute delegate as a single action and records it so it can later be undone.
RecordActionAsync(Action, Func<CancellationToken, ValueTask>, CancellationToken)Executes a synchronous execute delegate and an asynchronous unexecute delegate as a single action and records it so it can later be undone.
RecordActionAsync(Func<CancellationToken, ValueTask>, Action, CancellationToken)Executes an asynchronous execute delegate and a synchronous unexecute delegate as a single action and records it so it can later be undone.
RecordActionAsync(Func<CancellationToken, ValueTask>, Func<CancellationToken, ValueTask>, CancellationToken)Executes an asynchronous execute delegate and an asynchronous unexecute delegate as a single action and records it so it can later be undone.
RedoAsync(CancellationToken)Re-executes the most recently undone action. Does nothing when there is none.
UndoAsync(CancellationToken)Reverts the most recently recorded action. Does nothing when there is none.

Empties both the undo and redo buffers.

public void Clear()

Exceptions

InvalidOperationException
An action is currently executing, or a transaction is open.
UndoRedoNotificationException
A UndoRedoManager.HistoryChanged handler failed. The history was cleared.

CreateTransaction(TransactionExecutionMode)

UndoRedoManager.cs

Begins a transaction that groups subsequent recorded actions into a single undo step.

Parameters

mode TransactionExecutionMode
Whether the recorded actions run when the transaction is committed (TransactionExecutionMode.Deferred, the default) or as they are recorded (TransactionExecutionMode.Immediate).

Returns

UndoRedoTransaction A transaction that must be committed with UndoRedoTransaction.CommitAsync to be kept. Disposing it rolls it back unless it was already completed. Nested transactions must use the same mode as the transaction they are nested in, and must be completed from the innermost out.

Exceptions

InvalidOperationException
An action is currently executing, or mode differs from the enclosing transaction's mode.

RecordActionAsync overloads

RecordActionAsync(IUndoRedoAction, CancellationToken)

UndoRedoManager.cs

Executes an action and records it so it can later be undone.

net10.0

public ValueTask RecordActionAsync(IUndoRedoAction action, CancellationToken cancellationToken = default)

net11.0

public ValueTask RecordActionAsync(IUndoRedoAction action, CancellationToken cancellationToken = default)
Parameters
action IUndoRedoAction
The action to execute and record.
cancellationToken CancellationToken
A token to cancel the execution.
Returns

ValueTask

Exceptions
ArgumentNullException
action is null.
InvalidOperationException
An action is currently executing, or action is a transaction that is still open or belongs to another manager.
OperationCanceledException
cancellationToken is cancelled.
UndoRedoNotificationException
A UndoRedoManager.HistoryChanged handler failed. The action was recorded.

RecordActionAsync(Action, Action, CancellationToken)

UndoRedoManager.cs

Executes a synchronous execute delegate and a synchronous unexecute delegate as a single action and records it so it can later be undone.

public ValueTask RecordActionAsync(Action execute, Action unexecute, CancellationToken cancellationToken = default)
Parameters
execute Action
The delegate invoked to apply the change.
unexecute Action
The delegate invoked to revert the change.
cancellationToken CancellationToken
A token to cancel the execution.
Returns

ValueTask

Exceptions
ArgumentNullException
execute or unexecute is null.

RecordActionAsync(Action, Func<CancellationToken, ValueTask>, CancellationToken)

UndoRedoManager.cs

Executes a synchronous execute delegate and an asynchronous unexecute delegate as a single action and records it so it can later be undone.

public ValueTask RecordActionAsync(Action execute, Func<CancellationToken, ValueTask> unexecute, CancellationToken cancellationToken = default)
Parameters
execute Action
The delegate invoked to apply the change.
unexecute Func<CancellationToken, ValueTask>
The delegate invoked to revert the change.
cancellationToken CancellationToken
A token to cancel the execution.
Returns

ValueTask

Exceptions
ArgumentNullException
execute or unexecute is null.

RecordActionAsync(Func<CancellationToken, ValueTask>, Action, CancellationToken)

UndoRedoManager.cs

Executes an asynchronous execute delegate and a synchronous unexecute delegate as a single action and records it so it can later be undone.

public ValueTask RecordActionAsync(Func<CancellationToken, ValueTask> execute, Action unexecute, CancellationToken cancellationToken = default)
Parameters
execute Func<CancellationToken, ValueTask>
The delegate invoked to apply the change.
unexecute Action
The delegate invoked to revert the change.
cancellationToken CancellationToken
A token to cancel the execution.
Returns

ValueTask

Exceptions
ArgumentNullException
execute or unexecute is null.

RecordActionAsync(Func<CancellationToken, ValueTask>, Func<CancellationToken, ValueTask>, CancellationToken)

UndoRedoManager.cs

Executes an asynchronous execute delegate and an asynchronous unexecute delegate as a single action and records it so it can later be undone.

public ValueTask RecordActionAsync(Func<CancellationToken, ValueTask> execute, Func<CancellationToken, ValueTask> unexecute, CancellationToken cancellationToken = default)
Parameters
execute Func<CancellationToken, ValueTask>
The delegate invoked to apply the change.
unexecute Func<CancellationToken, ValueTask>
The delegate invoked to revert the change.
cancellationToken CancellationToken
A token to cancel the execution.
Returns

ValueTask

Exceptions
ArgumentNullException
execute or unexecute is null.

RedoAsync(CancellationToken)

UndoRedoManager.cs

Re-executes the most recently undone action. Does nothing when there is none.

net10.0

public ValueTask RedoAsync(CancellationToken cancellationToken = default)

net11.0

public ValueTask RedoAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken
A token to cancel the operation.

Returns

ValueTask

Exceptions

InvalidOperationException
An action is currently executing, or a transaction is open.
OperationCanceledException
cancellationToken is cancelled.
AggregateException
A grouped action failed and reverting the actions that already ran failed too.
UndoRedoNotificationException
A UndoRedoManager.HistoryChanged handler failed. The action was re-executed.

UndoAsync(CancellationToken)

UndoRedoManager.cs

Reverts the most recently recorded action. Does nothing when there is none.

net10.0

public ValueTask UndoAsync(CancellationToken cancellationToken = default)

net11.0

public ValueTask UndoAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken
A token to cancel the operation.

Returns

ValueTask

Exceptions

InvalidOperationException
An action is currently executing, or a transaction is open.
OperationCanceledException
cancellationToken is cancelled.
AggregateException
A grouped action failed to revert and re-applying the actions already reverted failed too.
UndoRedoNotificationException
A UndoRedoManager.HistoryChanged handler failed. The action was reverted.

Events

NameDescription
HistoryChangedOccurs when the undo/redo buffers change (an action is recorded, undone, redone, or the history is cleared).
PropertyChangedOccurs when a property value changes.

HistoryChanged

Occurs when the undo/redo buffers change (an action is recorded, undone, redone, or the history is cleared).

public event EventHandler? HistoryChanged

Remarks

A handler that throws surfaces to the caller as an UndoRedoNotificationException, after the operation itself has completed.

PropertyChanged

Occurs when a property value changes.

public event PropertyChangedEventHandler? PropertyChanged