Search code examples
c#windows-phone-8mvvmcross

MvvmCross backward navigation on Windows Phone 8.1


I have a Windows Phone Silverlight 8.1 MVVMCross 3.5.1 based application that navigates forward and backward between views flawlessly.

To navigate forward, I am using the

MvxViewModel.ShowViewModel()

API. This works well. To navigate backwards, I simply use the back button on the phone and this also works well.

I am making a Windows Phone 8.1 equivalent of the same application to use some wallet features. Navigation forward works as expected. However, when I click on the back button on the phone, application exits.

This is the entire content of App.xaml.cs for the Windows Phone 8.1 app.

 /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    public sealed partial class App : Application
    {
        private TransitionCollection transitions;

        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            this.InitializeComponent();
            this.Suspending += this.OnSuspending;
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used when the application is launched to open a specific file, to display
        /// search results, and so forth.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {

#if DEBUG
        if (System.Diagnostics.Debugger.IsAttached)
        {
            this.DebugSettings.EnableFrameRateCounter = true;
        }
#endif

        Frame rootFrame = Window.Current.Content as Frame;

        // Do not repeat app initialization when the Window already has content,
        // just ensure that the window is active
        if (rootFrame == null)
        {
            // Create a Frame to act as the navigation context and navigate to the first page
            rootFrame = new Frame();

            // TODO: change this value to a cache size that is appropriate for your application
            rootFrame.CacheSize = 1;

            if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
            {
                // TODO: Load state from previously suspended application
            }

            // Place the frame in the current Window
            Window.Current.Content = rootFrame;
        }

        if (rootFrame.Content == null)
        {
            // When the navigation stack isn't restored navigate to the first page,
            // configuring the new page by passing required information as a navigation
            // parameter

            var setup = new Setup(rootFrame);
            setup.Initialize();



            //// Removes the turnstile navigation for startup.
            //if (rootFrame.ContentTransitions != null)
            //{
            //    this.transitions = new TransitionCollection();
            //    foreach (var c in rootFrame.ContentTransitions)
            //    {
            //        this.transitions.Add(c);
            //    }
            //}

            //rootFrame.ContentTransitions = null;
            //rootFrame.Navigated += this.RootFrame_FirstNavigated;

            //// When the navigation stack isn't restored navigate to the first page,
            //// configuring the new page by passing required information as a navigation
            //// parameter
            //if (!rootFrame.Navigate(typeof(MainPage), e.Arguments))
            //{
            //    throw new Exception("Failed to create initial page");
            //}

            var starter = Cirrious.CrossCore.Mvx.Resolve<Cirrious.MvvmCross.ViewModels.IMvxAppStart>();
            starter.Start();
        }

        // Ensure the current window is active
        Window.Current.Activate();
    }

    /// <summary>
    /// Restores the content transitions after the app has launched.
    /// </summary>
    /// <param name="sender">The object where the handler is attached.</param>
    /// <param name="e">Details about the navigation event.</param>
    private void RootFrame_FirstNavigated(object sender, NavigationEventArgs e)
    {
        var rootFrame = sender as Frame;
        rootFrame.ContentTransitions = this.transitions ?? new TransitionCollection() { new NavigationThemeTransition() };
        rootFrame.Navigated -= this.RootFrame_FirstNavigated;
    }

    /// <summary>
    /// Invoked when application execution is being suspended.  Application state is saved
    /// without knowing whether the application will be terminated or resumed with the contents
    /// of memory still intact.
    /// </summary>
    /// <param name="sender">The source of the suspend request.</param>
    /// <param name="e">Details about the suspend request.</param>
    private void OnSuspending(object sender, SuspendingEventArgs e)
    {
        var deferral = e.SuspendingOperation.GetDeferral();

        // TODO: Save application state and stop any background activity
        deferral.Complete();
    }
}

What am I missing?


Solution

  • Windows Phone 8.1 does some stuff with your navigation that prevent it from working correctly.

    These issue's contain a lot of information on this topic: https://github.com/MvvmCross/MvvmCross/pull/760 https://github.com/MvvmCross/MvvmCross/issues/1018 https://gist.github.com/Cheesebaron/3d33daf4b76dc091e26e

    The code you could use as base class is:

    public abstract class BaseView<T> : MvxWindowsPage<T> where T : MvxViewModel
    {
        private ICommand _goBackCommand;
    
        public ICommand GoBackCommand
        {
            get { return _goBackCommand ?? (_goBackCommand = new MvxCommand(GoBack, CanGoBack)); }
            set { _goBackCommand = value; }
        }
    
        protected BaseView()
        {
            NavigationCacheMode = NavigationCacheMode.Required;
            Loaded += (s, e) => {
                HardwareButtons.BackPressed += HardwareButtonsBackPressed;
            };
    
            Unloaded += (s, e) => {
                HardwareButtons.BackPressed -= HardwareButtonsBackPressed;
            };
        }
    
        private void HardwareButtonsBackPressed(object sender, BackPressedEventArgs e)
        {
            if (GoBackCommand.CanExecute(null))
            {
                e.Handled = true;
                GoBackCommand.Execute(null);
            }
        }
    
        public virtual void GoBack()
        {
            if (Frame != null && Frame.CanGoBack) Frame.GoBack();
        }
    
        public virtual bool CanGoBack()
        {
            return Frame != null && Frame.CanGoBack;
        }
    
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (e.NavigationMode == NavigationMode.New)
                ViewModel = null;
    
            base.OnNavigatedTo(e);
        }
    }