2
votes

I'm using the Prism MVVM library for WinRT in a Windows Phone 8.1 project. Is it possible to prevent back navigation via the phone's back button and handle the back button press in the ViewModel?

Concrete scenario:

  • The user can select one item (the "active" item) from a list of items - like a player in a game. That item is a reference for the rest of the app's functionality, e.g. database queries.
  • Selecting one item returns the user to the previous (main) page.
  • In the same list, the user can also delete an item that is no longer needed. It should be possible to delete all items.

Problem: If the user deletes the active item or the last item and then taps the back button, I end up having an invalid active item.

To prevent that, I'd like to cancel the back button navigation and prompt the user to select or create another active item, ideally from the ViewModel.


Update: I have now added an event handler to the App.xaml.cs based on how I understood Nate's comment below. This should override it application-wide:

private void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
    Frame currentFrame = Window.Current.Content as Frame;
    if (currentFrame == null)
    {
        return;
    }
    if (currentFrame.Content is SelectionPage)
    {
        e.Handled = true;
    }
    else if (currentFrame.CanGoBack)
    {
        currentFrame.GoBack();
        e.Handled = true;
    }
}

and subscribing to the event in the constructor:

#if WINDOWS_PHONE_APP
        HardwareButtons.BackPressed += HardwareButtons_BackPressed;
#endif

This seems to be handling the back button press just fine but it does not stop the existing navigation. So it goes back in any case and twice in the default case.

1
You have to cancel the back button navigation on the platform in the app itself using HardwareButtons.BackPressed. You can then do whatever logic you want with it, whether it be app-wide logic or page-specific. Make sure to set Handled to true in the event args.Nate Diamond
@NateDiamond Thank you for your help. Do I understand that this means knocking out the entire navigation service provided by Prism on application level? That seems pretty radical. Would it also move the navigation logic that uses information from the ViewModel into the View? Isn't there a way to override the back navigation for a specific page within the Prism framework?jerry
For anyone wondering about the same issue: there is of course a workaround, handling this in the OnNavigatedTo() of the page returned to, bouncing the user back to the selection page. Doesn't look clean though.jerry
If you add an event in app.xaml.cs, then yes it will intercept it in the whole app. If, however, you intercept it only on pages and remove it when leaving the page, then it won't disrupt the whole navigation system.Nate Diamond
Is it the VisualStateAwarePage? If so, you may not be able to use it, or you may need to make your own page subclass.Nate Diamond

1 Answers

3
votes

This is possible. Here is the solution (mostly inspired by this discussion):

Create an interface that allows view models to disable the back navigation:

public interface IRevertState
{
    bool CanRevertState();
    void RevertState();
}

In the view model implement the interface:

public class myViewModel : ViewModel, IRevertState {
public bool CanRevertState() {
    return (...) //condition under which back navigation should be disabled
}
public void RevertState() {
    (...) // optionally reset condition if required
}

In App.Xaml.cs handle the back navigation:

#if WINDOWS_PHONE_APP
    protected override void OnHardwareButtonsBackPressed(object sender, BackPressedEventArgs e) {
        var page = (Page)((Frame)Window.Current.Content).Content;
        if (page.DataContext is IRevertState) {
            var revertable = (IRevertState)page.DataContext;
            if (revertable.CanRevertState()) {
                revertable.RevertState();
                e.Handled = true;
                return;
            }
        }
        base.OnHardwareButtonsBackPressed(sender, e);
    }
#endif