Search code examples
c#uwpwindows-store-appswindows-10-universaluwp-xaml

e.Cancel cannot be set after awaiting async dialog in UWP


This is for a Windows 10 UWP app. When the user tries to navigate away from a page, I want to have the user confirm if he wants to save the current data.

I have overridden OnNavigatingFrom as shown below. However, after the async MessageDialog, setting e.Cancel=false doesn't work. The page still stays on the current page even e.Cancel is later set to false. Please help!

protected override async void OnNavigatingFrom(NavigatingCancelEventArgs e)

{
    e.Cancel = true; //if I don't put this at the top, the page navigates right away

    var yesCommand = new UICommand("Yes", async cmd => {

        try
        {
            await SaveWorkshetItem(false);
            e.Cancel = false;
        }
        catch (Exception ex)
        {
            await new MessageDialog("Error saving Worksheet Item. Please contact you administrator." + ex.Message + Environment.NewLine + ex.StackTrace).ShowAsync();
        }

    });

    var noCommand = new UICommand("No", cmd => { e.Cancel = false; });

    var cancelCommand = new UICommand("Cancel", cmd => { e.Cancel = true;  });

    var dialog = new MessageDialog("Do you want to save the current item before navigating away?");
    dialog.Options = MessageDialogOptions.None;
    dialog.Commands.Add(yesCommand);

    dialog.Commands.Add(noCommand);
    dialog.Commands.Add(cancelCommand);

    await dialog.ShowAsync();

    base.OnNavigatingFrom(e);

}

To simplify this the below code causes the page to never leave even though I am changing back e.Cancel=false after the sample MessageDialog.

protected override async void OnNavigatingFrom(NavigatingCancelEventArgs e)

{
    e.Cancel = true; //if I don't put this at the top, the page navigates right away

    await new MessageDialog("Do you want to save the current item before navigating away?").ShowAsync();

    e.Cancel = false;  //unconditionally setting this back to false and it still won't leave the page

    base.OnNavigatingFrom(e);
}

Solution

  • To handle the navigation yourself, set Cancel=true (as you already do), then bring up the dialog to obtain user input. Once you know the user's choice, use the navigation APIs (e.g. Frame.GoBack) to perform the desired navigation (based on e.NavigationMode) if the user decided to allow the navigation to happen.

    Here is some basic sample code:

    private bool isNavigationConfirmed = false;
    protected async override void OnNavigatingFrom(NavigatingCancelEventArgs e)
    {
        base.OnNavigatingFrom(e);
        if (isNavigationConfirmed)
        {
            isNavigationConfirmed = false;
            return;
        }
        e.Cancel = true;
    
        var noCommand = new UICommand("No", cmd => { });
        var yesCommand = new UICommand("Yes", cmd =>
        {
            if (e.NavigationMode == NavigationMode.Back)
            {
                Frame.GoBack();
            }
            else
            {
                isNavigationConfirmed = true;
                Frame.Navigate(e.SourcePageType);
            }
        });
    
        var dialog = new MessageDialog("Do you want to allow navigation?");
        dialog.Options = MessageDialogOptions.None;
        dialog.Commands.Add(yesCommand);
        dialog.Commands.Add(noCommand);
        await dialog.ShowAsync();
    }