Undo and redo annotations
Nutrient UWP SDK supports the undo and redo functionality when working with annotations. Users can undo and redo their changes using either the corresponding built-in toolbar items — undo and redo — or the standard shortcut key combinations Control-Z (undo), and Control-Y (redo).
The undo and redo toolbar buttons are hidden by default. To display them in the toolbar, use the pdfView.SetToolbarItemsAsync()
API method.
To enable the undo and redo functionality, turn on the History API in one of the following ways:
-
Set
pdfView.IsHistoryEnabled
totrue
. -
Call the
History.EnableAsync()
API method.
Once the History API is enabled, you can disable it in one of the following ways:
-
Set
pdfView.IsHistoryEnabled
tofalse
. -
Call the
History.DisableAsync()
API method.
This example enables the History API in XAML and adds the default undo and redo buttons in the event handler for InitializationCompletedHandler
:
<ui:PdfView Name="PDFView" Grid.Row="0" IsHistoryEnabled="True" License="{StaticResource PSPDFKitLicense}" PdfUriSource="ms-appx:///Assets/pdfs/PSPDFKit.pdf" InitializationCompletedHandler="{x:Bind _viewModel.OnPDFViewInitializationCompleted}"/>
// In the `viewModel`. internal async void OnPDFViewInitializationCompleted(PdfView sender, Document args) { IList<IToolbarItem> toolbarItems = sender.GetToolbarItems(); toolbarItems.Add(new UndoToolbarItem()); toolbarItems.Add(new RedoToolbarItem()); await sender.SetToolbarItemsAsync(toolbarItems); }
Controlling undo and redo using the API
In addition to the dependency property mentioned above, the History API includes the following methods to perform different tasks related to this feature:
-
History.UndoAsync
— Rolls back the last annotation change — either a creation, an update, or a deletion — performed with the UI or the API. It returns aTask<bool>
instance that resolves totrue
when the operation succeeds. Otherwise, it returnsfalse
— for example, when there are no operations left to be undone.
This example uses the API to create an annotation, moves it, and then undoes the move:
var annotation = new Note { Contents = "PSPDFKit is awesome!", BoundingBox = new Rect(50,50,50,50) }; await pdfView.Document.CreateAnnotationAsync(annotation); Debug.WriteLine("Annotation created!"); createdAnnotation.BoundingBox = new Rect(100, 100, 50, 50); await pdfView.Document.UpdateAnnotationAsync(createdAnnotation); Debug.WriteLine("Annotation moved!"); await pdfView.Document.History.UndoAsync(); Debug.WriteLine("Annotation creation undone: annotation moved back to original position!");
-
History.RedoAsync
— Repeats the annotation change rolled back by the last undo operation. It can be performed with either the UI or the API. It returns aTask<bool>
instance that resolves totrue
when the operation succeeds. Otherwise, it returnsfalse
— for example, when there are no operations left to be done.
This example uses the API to create an annotation, delete it, undo the deletion, and delete it again by using the redo functionality:
var annotation = new Note { Contents = "PSPDFKit is awesome!", BoundingBox = new Rect(50,50,50,50) }; var createdAnnotation = await pdfView.Document.CreateAnnotationAsync(annotation); Debug.WriteLine("Annotation created!"); await pdfView.Document.DeleteAnnotationAsync(createdAnnotation.Id); Debug.WriteLine("Annotation deleted!"); await pdfView.Document.History.UndoAsync(); Debug.WriteLine("Annotation deletion undone: annotation restored!"); await pdfView.Document.History.RedoAsync(); Debug.WriteLine("Annotation deletion redone: annotation deleted!");
To redo an operation, an undo operation is required first.
-
History.CanUndoAsync
— Returnstrue
when there are operations that can be undone. Otherwise, it returnsfalse
:if (pdfView.Document.History.CanUndoAsync()) { pdfView.Document.History.UndoAsync(); }
-
History.CanRedoAsync
— Returnstrue
when there are operations that can be redone. Otherwise, it returnsfalse
:if (pdfView.Document.History.CanRedoAsync()) { pdfView.Document.History.RedoAsync(); }
-
History.ClearAsync
— Resets the list of possible operations to be undone and redone:var history = pdfView.Document.History; Debug.WriteLine(await history.CanUndoAsync()); // `false` Debug.WriteLine(await history.CanRedoAsync()); // `false` var annotation = new Note { Contents = "PSPDFKit is awesome!", BoundingBox = new Rect(50, 50, 50, 50) }; var createdAnnotation = await pdfView.Document.CreateAnnotationAsync(annotation); Debug.WriteLine("Annotation created!"); await pdfView.Document.DeleteAnnotationAsync(createdAnnotation.Id); Debug.WriteLine("Annotation deleted!"); Debug.WriteLine(await history.CanUndoAsync()); // `true` Debug.WriteLine(await history.CanRedoAsync()); // `false` await history.UndoAsync(); Debug.WriteLine(await history.CanUndoAsync()); // `true` Debug.WriteLine(await history.CanRedoAsync()); // `true` await history.ClearAsync(); Debug.WriteLine(await history.CanUndoAsync()); // `false` Debug.WriteLine(await history.CanRedoAsync()); // `false`
Any creation, update, or deletion performed either with the UI or the API that isn’t a result of undoing or redoing will clear the list of possible operations.
Tracking history events
Undo and redo operations can be tracked by listening to the following events:
-
UndoEvent
occurs after performing an undo operation, either with the API or with the UI. -
RedoEvent
occurs after performing a redo operation, either with the API or with the UI. -
HistoryChanged
occurs after performing an undo or a redo operation, either with the API or with the UI.
The event handlers for the above events will receive a History
object that raised the event, along with the HistoryEventArgs
object:
public sealed class HistoryEventArgs { /// <summary> /// Previous state of the annotation, or `null` if it's being restored. /// </summary> public IAnnotation Before { get; } /// <summary> /// Resulting state of the annotation, or `null` if it's being deleted. /// </summary> public IAnnotation After { get; } /// <summary> /// Action that resulted in the event i.e. `HistoryAction.Undo` or `HistoryAction.Redo/>`. /// </summary> public HistoryAction Action { get; } }
-
Cleared
occurs after the history is cleared by theHistory.ClearAsync
API call.
Unlike the event handlers of other events, event handlers of the Cleared
event only receive the corresponding History
object, and the EventArgs
parameters are set to null
.
You can subscribe or unsubscribe to these events at any moment — even when the History API is disabled.
Exceptions
Here’s a description of how the behavior of undo and redo may affect different elements of the SDK:
-
Annotation Z Order — The original annotation Z order won’t be restored for annotations that are deleted and later restored with undo. Those annotations will be recreated in the foreground.
-
Annotation Presets — Annotation presets aren’t affected by undo and redo operations, and they won’t be restored to a previous or following state as a result of undo or redo.