Class UndoManager
An UndoManager observes and records model and diagram changes in transactions and supports undo/redo operations.
Namespace: Northwoods.Go.Models
Assembly: Northwoods.GoDiagram.Avalonia.dll
Syntax
public class UndoManager
Remarks
You will need to set the IsEnabled property to true in order for the UndoManager to record changes and for users to perform an undo or a redo.
Typically an operation will call StartTransaction(string), make some changes to the Model<TNodeData, TNodeKey, TSharedData> and/or Diagram, and then call CommitTransaction(string). Any ChangedEvents that occur will be recorded in a Transaction object. If for some reason you do not wish to complete the transaction successfully, you can call RollbackTransaction() instead of CommitTransaction(string).
For convenience the Commit(Action<Diagram>, string) and Commit(Action<IModel>, string) methods execute a function within a transaction and then perform a commit, or else a rollback upon an error.
The History property is a list of Transactions. CommitTransaction(string) will add the CurrentTransaction to the History list. RollbackTransaction() will undo the changes remembered in the CurrentTransaction and then discard it, without changing the History. You can limit how many transactions are remembered in the history by setting MaxHistoryLength.
Transactions may be nested. Be sure to call either CommitTransaction(string) or RollbackTransaction() for each call to StartTransaction(string). Avoid repeated start-commit-start-commit calls as a result of a user's actions. Instead, start, make all changes, and then commit.
If you want to restore the diagram to the state before the latest complete transaction, call Undo(). Call Redo() to change the diagram to a later state. If after some number of undos you start a transaction, all of the history after the current state is discarded, and a new transaction may be recorded. You cannot undo or redo during a transaction.
Initially each IModel has its own UndoManager. UndoManagers may be shared by multiple Models by replacing the standard UndoManager created by the model constructor.
There are several informational properties:
- IsInTransaction is true when a top-level transaction has been started that has not yet been committed or rolled-back.
- CurrentTransaction holds the flattened list of all ChangedEvents that have happened within the current transaction.
- TransactionLevel indicates the current depth of nesting.
- NestedTransactionNames holds the stack of transaction names supplied to StartTransaction(string) calls.
- History holds only complete top-level transactions.
- IsUndoingRedoing is true during a call to Undo() or Redo().
- HistoryIndex indicates which Transaction in the History is the next to be "undone"; this is decremented by each undo and incremented by each redo.
- TransactionToUndo and TransactionToRedo indicate which Transaction may be undone or redone next, if any.
- Models returns an iterator over all of the IModels that this UndoManager is handling.
A transaction may not be ongoing when replacing a Model, because it would not make sense to be replacing the UndoManager (the UndoManager) while changes are being recorded.
Replacing a Model copies certain properties from the old UndoManager to the new one, including IsEnabled and MaxHistoryLength.
Constructors
UndoManager()
The constructor produces an empty UndoManager with no transaction history.
Declaration
public UndoManager()
Properties
CurrentTransaction
This read-only property returns the current Transaction for recording additional model change events.
Declaration
public Transaction CurrentTransaction { get; }
Property Value
Type | Description |
---|---|
Transaction |
Remarks
This is initialized and augmented by HandleChanged(ChangedEvent) before it is added to History by a top-level call to CommitTransaction(string). The value will be null between transactions.
History
This read-only property returns the whole history, a list of all of the Transactions, each representing a transaction with some number of ChangedEvents.
Declaration
public IList<Transaction> History { get; }
Property Value
Type | Description |
---|---|
IList<Transaction> |
Remarks
You should not modify this List.
HistoryIndex
This read-only property returns the index into History for the current undoable Transaction.
Declaration
public int HistoryIndex { get; }
Property Value
Type | Description |
---|---|
int |
Remarks
The value is -1 if there is no undoable Transaction to be undone.
IsEnabled
Gets or sets whether this UndoManager records any changes.
Declaration
public bool IsEnabled { get; set; }
Property Value
Type | Description |
---|---|
bool |
Remarks
The default value is false -- you need to set this to true if you want the user to be able to undo or redo.
You can temporarily turn off recording by setting SkipsUndoManager and SkipsUndoManager to true.
IsInTransaction
This read-only property is true after the first call to StartTransaction(string) and before a corresponding call to CommitTransaction(string) or RollbackTransaction().
Declaration
public bool IsInTransaction { get; }
Property Value
Type | Description |
---|---|
bool |
Remarks
During a transaction CanUndo() and CanRedo() will be false. CurrentTransaction may be non-null if any ChangedEvents were recorded.
IsUndoingRedoing
Declaration
public bool IsUndoingRedoing { get; }
Property Value
Type | Description |
---|---|
bool |
MaxHistoryLength
Gets or sets the maximum number of transactions that this undo manager will remember.
Declaration
public double MaxHistoryLength { get; set; }
Property Value
Type | Description |
---|---|
double |
Remarks
When a transaction is committed and the number exceeds this value, the UndoManager will discard the oldest transaction(s) in order to meet this limit. The initial value is 999. Any new value must be an integer. A negative value is treated as if there were no limit. A zero value will not remember any Transactions in the History, but will allow commits and rollbacks to occur normally, including raising "Transaction" type ChangedEvents.
This property is useful in helping limit the memory consumption of typical applications. But this does not limit the number of ChangedEvents that are recorded, because there may be an unlimited number of those within each Transaction. Decreasing this value will not necessarily remove any existing Transactions if there currently exist more in History than the new value permits.
Models
This read-only property returns an iterator for all of the Model<TNodeData, TNodeKey, TSharedData>s that this UndoManager is handling.
Declaration
public IEnumerable<IModel> Models { get; }
Property Value
Type | Description |
---|---|
IEnumerable<IModel> |
See Also
NestedTransactionNames
This read-only property returns a stack of ongoing transaction names.
Declaration
public IList<string> NestedTransactionNames { get; }
Property Value
Type | Description |
---|---|
IList<string> |
Remarks
The outermost transaction name will be the first item in the list. The last one will be the name of the most recent (nested) call to StartTransaction(string).
You should not modify this List.
TransactionLevel
This read-only property returns the current transaction level.
Declaration
public double TransactionLevel { get; }
Property Value
Type | Description |
---|---|
double |
Remarks
The value is zero when there is no ongoing transaction. The initial value is zero. StartTransaction(string) will increment this value; CommitTransaction(string) or RollbackTransaction() will decrement it. When this value is greater than zero, CanUndo() and CanRedo() will be false, because additional logically related model change events may occur.
TransactionToRedo
This read-only property returns the Transaction in the History to be redone next.
Declaration
public Transaction TransactionToRedo { get; }
Property Value
Type | Description |
---|---|
Transaction |
Remarks
The value may be null if the UndoManager is not ready to perform a redo.
See Also
TransactionToUndo
This read-only property returns the Transaction in the History to be undone next.
Declaration
public Transaction TransactionToUndo { get; }
Property Value
Type | Description |
---|---|
Transaction |
Remarks
The value may be null if the UndoManager is not ready to perform an undo.
See Also
Methods
AddModel(IModel)
Make sure this UndoManager knows about a IModel for which it may receive ChangedEvents when the given IModel is changed.
Declaration
public void AddModel(IModel model)
Parameters
Type | Name | Description |
---|---|---|
IModel | model | A IModel that this UndoManager is managing. |
Remarks
The model will also receive notifications about transactions and undo or redo operations.
You should not call this method during a transaction.
See Also
CanRedo()
This predicate returns true if you can call Redo().
Declaration
public bool CanRedo()
Returns
Type | Description |
---|---|
bool | true if ready for Redo() to be called. |
Remarks
This will return false if IsEnabled is false (as it is by default), if any transaction is ongoing, or if there is no TransactionToRedo that can be redone.
CanUndo()
This predicate returns true if you can call Undo().
Declaration
public bool CanUndo()
Returns
Type | Description |
---|---|
bool | true if ready for Undo() to be called. |
Remarks
This will return false if IsEnabled is false (as it is by default), if any transaction is ongoing, or if there is no TransactionToUndo that can be undone.
Clear()
Clear all of the Transactions and clear all other state, including any ongoing transaction without rolling back.
Declaration
public void Clear()
Remarks
However, this maintains its references to its IModels.
You should not call this method during a transaction.
CommitTransaction(string)
Commit the current transaction started by a call to StartTransaction(string).
Declaration
public bool CommitTransaction(string tname = "")
Parameters
Type | Name | Description |
---|---|---|
string | tname | a short string describing the transaction; this is recorded as the Name and need not be the same as the string passed to StartTransaction(string). If the value is an empty string or not supplied, this will use the name given to StartTransaction(string). |
Returns
Type | Description |
---|---|
bool | true if ending a top-level transaction. |
Remarks
For convenience, this method is called by CommitTransaction(string) and CommitTransaction(string).
If this call stops a top-level transaction, we mark the CurrentTransaction as complete (IsComplete), we add the Transaction to the History list, and we return true. Committing a transaction when there have been some undos without corresponding redos will throw away the Transactions holding changes that happened after the current state, before adding the new Transaction to the History list.
See Also
HandleChanged(ChangedEvent)
Maybe record a ChangedEvent in the CurrentTransaction.
Declaration
public void HandleChanged(ChangedEvent e)
Parameters
Type | Name | Description |
---|---|---|
ChangedEvent | e | a ChangedEvent. |
Remarks
This calls SkipsEvent(ChangedEvent) to see if this should ignore the change. If SkipsEvent(ChangedEvent) returns false, this creates a copy of the ChangedEvent and adds it to the CurrentTransaction. If there is no CurrentTransaction, this first creates and remembers it.
This method always ignores all changes while performing an Undo() or Redo(). This method is also a no-op if IsEnabled is false.
Redo()
After an Undo(), re-perform the changes in TransactionToRedo.
Declaration
public void Redo()
Remarks
CanRedo() must be true for this method to have any effect.
This is called by Redo().
This will raise a "StartingRedo" ChangedEvent of type Transaction, perform the Redo() on the TransactionToRedo, and then raise a "FinishedRedo" ChangedEvent of type Transaction. The two ChangedEvents are to let model listeners know that a redo is about to take place and that it just finished. IsUndoingRedoing will temporarily be set to true during this operation.
RemoveModel(IModel)
Inform this UndoManager that it will no longer be receiving ChangedEvents when the given IModel is changed.
Declaration
public void RemoveModel(IModel model)
Parameters
Type | Name | Description |
---|---|---|
IModel | model | A IModel that this UndoManager should no longer manage. |
Remarks
The model will no longer receive notifications about transactions and undo or redo operations.
You should not call this method during a transaction. If you call this method between transactions when there is a transaction history, you should be careful that there are no ChangedEvents referring to that model in any Transactions.
See Also
RollbackTransaction()
Rollback the current transaction started by a call to StartTransaction(string), undoing any changes.
Declaration
public bool RollbackTransaction()
Returns
Type | Description |
---|---|
bool | true if ending a top-level transaction. |
Remarks
For convenience, this method is called by RollbackTransaction() and RollbackTransaction().
This undoes and then discards the changes in the CurrentTransaction. You must have started a transaction previously.
See Also
SkipsEvent(ChangedEvent)
This predicate is called by HandleChanged(ChangedEvent) to decide if a ChangedEvent is not interesting enough to be remembered.
Declaration
public bool SkipsEvent(ChangedEvent e)
Parameters
Type | Name | Description |
---|---|---|
ChangedEvent | e | the ChangedEvent received by HandleChanged(ChangedEvent). |
Returns
Type | Description |
---|---|
bool | true to not record the change. |
Remarks
Transactional events (of change type Transaction) are always skipped. Changed events for GraphObjects that are in IsTemporary layers are also skipped.
Sometimes changed events do not even get to HandleChanged(ChangedEvent) because SkipsUndoManager or SkipsUndoManager is true.
StartTransaction(string)
Begin a transaction, where the changes are held by a Transaction object as the value of CurrentTransaction.
Declaration
public bool StartTransaction(string tname = null)
Parameters
Type | Name | Description |
---|---|---|
string | tname | a short string describing the transaction, pushed onto the NestedTransactionNames stack. |
Returns
Type | Description |
---|---|
bool | true if starting a top-level transaction. |
Remarks
You must call either CommitTransaction(string) or RollbackTransaction() afterwards.
For convenience, this method is called by StartTransaction(string) and StartTransaction(string).
Transactions can be nested. Starting or ending a nested transaction will return false. Nested transactions will share the same Transaction list of ChangedEvents.
Starting a transaction will not necessarily cause CurrentTransaction to be non-null. A Transaction object is usually only created by HandleChanged(ChangedEvent) when a ChangedEvent first occurs.
See Also
Undo()
Reverse the effects of the TransactionToUndo.
Declaration
public void Undo()
Remarks
CanUndo() must be true for this method to have any effect.
This is called by Undo().
This will raise a "StartingUndo" ChangedEvent of type Transaction, perform the Undo() on the TransactionToUndo, and then raise a "FinishedUndo" ChangedEvent of type Transaction. The two ChangedEvents are to let model listeners know that an undo is about to take place and that it just finished. IsUndoingRedoing will temporarily be set to true during this operation.