LeaveConfirmation class

Namespace Runic.Navigation · Runic.Navigation 0.7.0-preview.5

public sealed class LeaveConfirmation

A departure guard that asks before unsaved changes are lost, and discards them only when the departure commits. Compose it into a ViewModel that implements INavigationDepartureGuard and forward INavigationDepartureGuard.CanDepartAsync to LeaveConfirmation.CanDepartAsync.

Remarks

A yes stands while the transition it allowed is unsettled: a guard that runs again for the same entry, for example when a parent transition asks a child's guard that a child transition already asked, does not ask again. When a later request supersedes that transition, the yes moves to the later request. When the transition commits, is rejected, is cancelled or fails, the yes ends and the next departure asks again. When a change in another region (rather than a later request) supersedes it, the yes also ends, so the user may be asked again.

discard runs at most once per yes, inside the commit turn of the departure, after the new navigation state is applied and before the regions raise PropertyChanged. It must not pump or await navigation.

LeaveConfirmation.CanDepartAsync continues on the caller's synchronization context, so a confirm delegate that a UI-thread guard calls (a message box or a modal dialog) runs on that thread.

Constructors

LeaveConfirmation

public LeaveConfirmation(Func<bool> hasUnsavedChanges, Func<NavigationDeparture, CancellationToken, ValueTask<bool>> confirm, Action? discard = null, bool askOnRetain = false)

Creates a leave confirmation.

Parameters

hasUnsavedChanges

Returns whether leaving would lose changes. It is called inside a model turn.

confirm

Asks the user and returns whether to leave. It runs in the guard, outside model turns, with the guard's token, which is cancelled when the departure is superseded, cancelled or the navigator closes.

discard

Discards the changes. It runs in the commit turn of a confirmed departure, at most once per yes.

askOnRetain

Whether to ask when the entry is only retained, for example when a push covers it. By default only a departure that retires the entry asks, because a retained entry keeps its state. With it, a confirmed push over the entry runs discard when the push commits, although the entry stays in the history.

Methods

CanDepartAsync

public ValueTask<bool> CanDepartAsync(NavigationDeparture departure, CancellationToken cancellationToken)

Decides a departure: allows a retaining departure unless askOnRetain is set, allows a departure that a standing yes covers, allows when there are no unsaved changes, and otherwise asks. A yes registers discard with NavigationDeparture.OnCommitted.

Parameters

departure

The departure the guard received.

cancellationToken

The guard's token.

InDialog

public static LeaveConfirmation InDialog<TDialog>(NavigationRegion<TDialog> dialogs, Func<INavigationTarget<TDialog>> dialog, Func<bool> hasUnsavedChanges, Action? discard = null) where TDialog : class

Creates a leave confirmation that asks with a dialog pushed for a Boolean result into dialogs. Only Completed with true confirms; a dismissal, a rejected push or false keeps the entry. The guard's token is passed to the push, so a superseded departure and a closing navigator dismiss the dialog.

Type parameters

TDialog

The dialog region's content type.

Parameters

dialogs

The region that presents the dialog.

dialog

Creates the dialog's target, once per question.

hasUnsavedChanges

Returns whether leaving would lose changes. It is called inside a model turn.

discard

Discards the changes in the commit turn of a confirmed departure.

Remarks

The dialog region must not be the guarded region, one of its ancestors or one of its descendants: the push would be NavigationRejection.Reentrant, its completion Dismissed, and the guard would veto. The dialog completes with NavigationEntryContext.CompleteAsync and cancels with NavigationEntryContext.DismissAsync.