/// <summary> /// Creates a new NavigationService for a Frame, registering the frame and NavigationService /// for future retrieval and registering the frame with the NavigationContextService. /// </summary> /// <param name="frame">The frame to register.</param> /// <param name="defaultPage">The default page to navigate to if there is no state to restore.</param> /// <param name="defaultNavigationContext">The navigation context to provide on navigation to defaultPage.</param> /// <param name="restoreState">True to attempt to restore previously saved state, false to just navigate to defaultPage /// without attempting to restore state.</param> /// <returns>The NavigationService for the registered frame.</returns> public static INavigationService RegisterFrame(Frame frame, Type defaultPage, NavigationContextBase defaultNavigationContext = null, bool restoreState = true) { if (frame == null) { throw new ArgumentNullException(nameof(frame)); } if (defaultPage == null) { throw new ArgumentNullException(nameof(defaultPage)); } if (GetNavigationService(frame) != null) { throw new InvalidOperationException("Frame already associated with a NavigationService."); } // Clean up references before adding a new one. RemoveFrame(); NavigationService navigationService = new NavigationService(frame, new NavigationContextService()); // Ensure navigationService is fully registered before restoring state // or navigating so that it can be accessed by PageBase. NavigationServices.Add(navigationService); if (restoreState) { // This triggers NavigatedTo on PageBase if there is state restored. navigationService.RestoreState(); } // If state wasn't restored, navigate to a default page to populate the Frame. if (navigationService.RootFrame.Content == null) { navigationService.Navigate(defaultPage, defaultNavigationContext); } return navigationService; }
/// <summary> /// Called when the page is navigated to. Initializes properties, such as /// the navigation service and view model, as well as restores context /// from the suspension manager. /// </summary> /// <param name="e">The event args.</param> protected override async void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); if (e == null) { return; } // Get NavigationService if (this.navigationService == null) { this.navigationService = NavigationService.GetNavigationService(this.Frame) as NavigationService; if (this.navigationService == null) { throw new InvalidOperationException("Frame not registered before page navigation."); } this.contextService = this.navigationService.ContextService; Debug.Assert(this.contextService != null, "A navigation service's context service should never be null."); } // Set ViewModel if (this.ViewModel == null) { INavigatableViewModel viewModel = this.DataContext as INavigatableViewModel; if (viewModel == null) { throw new InvalidOperationException("ViewModel must implement INavigatableViewModel interface."); } this.ViewModel = viewModel; } // Load page state if this page wasn't already cached by the frame IDictionary<string, PageState> pageStates = this.navigationService.PageStates; this.pageKey = "Page-" + this.Frame.BackStackDepth; // Get the NavigationContext NavigationContextBase context = this.GetContextFromParameter(e.Parameter); PageState pageState = null; if (e.NavigationMode == NavigationMode.New) { // Clear existing state for forward navigation when adding a new page to the // navigation stack var nextPageKey = this.pageKey; int nextPageIndex = this.Frame.BackStackDepth; while (pageStates.Remove(nextPageKey)) { nextPageIndex++; nextPageKey = "Page-" + nextPageIndex; } // TODO: Also clean up stored navigation contexts when removing forward stack. } else { // Load page state using the same strategy for loading suspended state and // recreating pages discarded from cache pageStates.TryGetValue(this.pageKey, out pageState); } // Activate the ViewModel await this.ViewModel.Activate(this.navigationService, context, pageState); }