Template10: Cancel navigation when triggered by Hamburger menu

Created on 28 Jul 2016  路  6Comments  路  Source: Windows-XAML/Template10

I'm using the Hamburger template and I navigate to my pages through the Hamburger menu (from the Shell).

I have overrided OnNavigatingFromAsync in my corresponding ViewModel but when I set args.Cancel = true it does prevent the navigation but the page I was navigating to is still highlighted in the shell.

Worst, when I set a delay before args.Cancel = true, it does not prevent the navigation at all, even if I get the deferral and complete it after the delay.

backlog bug

Most helpful comment

I've also encountered this when giving the user a choice to leave the current page and lose changes or stay on the current page.

I can half-solve it by setting args.Cancel = true without awaiting if there are changes, then calling an async method to display a MessageDialog - but then if the user wishes to continue with the navigation I'm having trouble figuring out which page they were trying to navigate to (unless they were trying to go back), so that I can then use the NavigationService to complete the navigation.

Is there an easy way to reference this? I've got a temporary work-around which involves adding a RequestedPage property to the NavigationService - then setting this in NavigateAsync(), but I was hoping there might be a cleaner way.

Also, for the highlighting problem, how do I call NavMenu.Highlight() ? I can't find this anywhere.

All 6 comments

Yeah, turns out when you introduce an await in NavingFrom() Cancel no longer works because the NavService is using events instead of rewriting the underlying nav pipeline, but we're going to fix that in the next version. That being said, the highlight thing is definitely a problem. You can work around it by calling NavMenu.Highlight() but it needs to be fixed. Consider it a bug.

I've also encountered this when giving the user a choice to leave the current page and lose changes or stay on the current page.

I can half-solve it by setting args.Cancel = true without awaiting if there are changes, then calling an async method to display a MessageDialog - but then if the user wishes to continue with the navigation I'm having trouble figuring out which page they were trying to navigate to (unless they were trying to go back), so that I can then use the NavigationService to complete the navigation.

Is there an easy way to reference this? I've got a temporary work-around which involves adding a RequestedPage property to the NavigationService - then setting this in NavigateAsync(), but I was hoping there might be a cleaner way.

Also, for the highlighting problem, how do I call NavMenu.Highlight() ? I can't find this anywhere.

I'm having the same problem described by @elstringo .
I've noticed this in NavigationService 禄 NavigatingFromAsync
await dataContext.OnNavigatingFromAsync(args).ConfigureAwait(false);

Could it be that changing the ConfigureAwait to true solves the navigation cancellation issue ?

@elstringo I am having the same problem and trying your solution. How do you set the RequestedPage property? I am also interested to know how to call NavMenu.Highlight(). Cheers.

@Monsok
Since I couldn't find another way around it, I included the T10 Library Project in my Visual Studio Solution instead of the nuget reference, then added the following property to INavigationService
Type LastRequestedPageType { get; }
and then in NavigationService I've added a private setter to the property which I am then assigning to in the last few lines of NavigateAsync.

I've still not had any luck finding NavMenu.Highlight(), however, I have managed to overcome my particular highlighting issue by setting Views.Shell.HamburgerMenu.Selected = null; if the navigation is cancelled, This works for me since my ViewModel doesn't correspond to a Hamburger menu button, so I just need to make sure that none of the menu buttons are highlighted

@elstringo Thanks for the reply it was helpful. In my case I want to still highlight the current Hamburger menu button if user decided not to navigate away. And I feel like I do not want to override or add new property to INavigationService so based on your ideas, I added two new properties to my BaseViewModel

    private HamburgerButtonInfo pageHamburgerButtonInfo;
    public virtual HamburgerButtonInfo PageHamburgerButtonInfo
    {
        get { return pageHamburgerButtonInfo; }
        set { SetProperty(ref pageHamburgerButtonInfo, value); }
    }

    private HamburgerButtonInfo requestedHamburgerButtonInfo;
    public virtual HamburgerButtonInfo RequestedHamburgerButtonInfo
    {
        get { return requestedHamburgerButtonInfo; }
        set { SetProperty(ref requestedHamburgerButtonInfo, value); }
    }

Since I cannot access my Shell.HamburgerMenu directly from my ViewModel, I set the PageHamburgerButtonInfo from my Page code behind.

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        ViewModel.PageHamburgerButtonInfo = Shell.HamburgerMenu.Selected;
        ...

Then in my Shell page code behind, I set the RequestedHamburgerButtonInfo on SelectedChanged event. I only have one page that should show the prompt so I just get the corresponding ViewModel directly.

    private void HamburgerMenu_SelectedChanged(object sender, ChangedEventArgs<HamburgerButtonInfo> e)
    {
        ServiceLocator.Current.GetInstance<TextifierPageViewModel>().RequestedHamburgerButtonInfo = e.NewValue;
    }

Then to check if there's unsaved image and whether to show the prompt or not, in my page code behind I do the following:

    protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
    {
        if (ViewModel.OriginalImageProvider != null)
        {
            e.Cancel = true;
            canNavigateAwayAsync();
        }
        base.OnNavigatingFrom(e);
    }

    private async void canNavigateAwayAsync()
    {
        ContentDialog dialog = new ContentDialog()
        {
            Title = "Image Not Saved",
            Content = "Do you want to discard current image?",
            PrimaryButtonText = "Yes",
            SecondaryButtonText = "No"
        };

        var result = await dialog.ShowAsync();

        if (result == ContentDialogResult.Primary)
        {
            ViewModel.Clear();
            Shell.HamburgerMenu.NavigationService.Navigate(ViewModel.RequestedHamburgerButtonInfo.PageType);
        }
        else
        {
            Shell.HamburgerMenu.Selected = ViewModel.PageHamburgerButtonInfo;
        }
    }

If the user select No the selected HamburgerMenu is set back to the current page

Shell.HamburgerMenu.Selected = ViewModel.PageHamburgerButtonInfo;

Not the most elegant solution but it works for me for now...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Weasy666 picture Weasy666  路  8Comments

JerryNixon picture JerryNixon  路  9Comments

JerryNixon picture JerryNixon  路  7Comments

Joebeazelman picture Joebeazelman  路  5Comments

Opiumtm picture Opiumtm  路  8Comments