Skip to content
This repository has been archived by the owner on Aug 27, 2020. It is now read-only.

FormsNavigationPageService

Mark Smith edited this page Jun 4, 2018 · 8 revisions

FormsNavigationPageService

The FormsNavigationPageService provides a simple implementation of the INavigationService interface by using the built-in Xamarin.Forms NavigationPage support.

This service will attempt to assign a view model to the Page if one is not already assigned (it looks at the BindingContext and if it's null, assigns the passed state parameter) - see the creating view models page for more details. You can also provide async initialization for your view models through the IViewModelNavigationInit interface.

Events

  • Navigated : raised when a NavigateAsync occurs to a real page. This is not raised if the navigation is canceled or the page key does not exist. The sender for the event is the INavigationService.
  • NavigatedBack : raised when a GoBackAsync occurs and a page is popped off the navigation stack. The sender for the event is the INavigationService.

Properties

  • KeyComparer : this is an IEqualityComparer<object> implementation which is used to compare registered keys with the passed page keys for NavigateAsync. You only need to provide this if the key type you are using is not naturally comparable.

  • HideMasterPageOnNavigation : A boolean value which controls what happens when a NavigateAsync method is called and the root page is a MasterDetailPage. When true (the default), the Master page is automatically hidden on a phone device to force the details page to be displayed. When false, the current master page view state is left unchanged. Change this property value if you want to control the master/detail view display in your code.

Usage

Pages are identified with string-based keys - typically stored as constants in a class. You register a creator delegate function that returns a Page with the service and when the app requests to navigate to the given page-key, the delegate is called to return the page. That delegate could create a new page, return an existing page, or even cancel the navigation and perform some other action such as manipulate the MasterDetailPage.

To use this service, you must do several things:

  1. Register it as the implementation for INavigationService. This is typically done in the App constructor.
  2. Have a NavigationPage either as the MainPage, or as the first child of the "details" page of a MasterDetailPage.
  3. Register the pages you want to navigate to.
  4. Use the service to request navigation

Register the implementation

You can either call the static XamUInfrastructure.Init method to register the service, or register it yourself through your own container. Here's an example of manually registering the service with the built-in DependencyService:

public class App
{
   public App()
   {
      XamarinUniversity.Services.XamUInfrastructure.Init();
      ...
   }
}

or:

public class App
{
   public App()
   {
      DependencyService.Register<INavigationService, FormsNavigationPageService>();
      ...
   }
}

Use a NavigationPage

The Application main page must be a NavigationPage, or you can use a MasterDetailPage where the Detail property is set to a NavigationPage. If a NavigationPage is not found, an exception is thrown.

Register your pages and actions

You must register each unique page you want to navigate to with the service. This is commonly done in the App constructor. The RegisterPage method takes an object-based key and a delegate which will be called when a navigation is requested to that key - it must return a Page or null to cancel the navigation request and do nothing. If a Page is returned, then the class will use the Forms INavigation interface to navigate to the specified page.

Note: RegisterPage is not exposed on the interface to avoid the exposure of the Page type to the users of the abstraction. You must cast the returned interface to the implementation type to register your pages.

public enum AppPages
{
   Page1,
   Page2,
   ...
}

public class App
{
   public App()
   {
      DependencyService.Register<INavigationService,StackNavigationService>() as StackNavigationService;
      ...
      MasterDetailPage mdPage = ...;
  
      INavigationService navService = DependencyService.Get<INavigationService>();

      // Get the implementation so we can register our pages. Consumers just navigating to a page
      // won't do this - we only do this as part of our initialization. Probably should use "as" cast here
      // in real code and make sure that's what you got back too!
      var rnavService = (FormsNavigationPageService) navService;
      // Register a real page with the key
      rnavService.RegisterPage(AppPages.Page1, () => new Page1());
      // Do some other action on the Navigate - in this case, show the "Master" page.
      // Return null to cancel the NavigationService action.
      rnavService.RegisterPage(AppPages.Page2, () => { mdPage.IsPresented = true; return null; });
     ...
   }
}

Use the navigation service

Finally, in your VMs (or anywhere in your app) you can use the INavigationService to do the navigation using the registered page keys. You can also pass in an optional BindingContext for the page.

public class MyViewModel
{
   IDependencyService ds;

   async Task OnShowPage1Async() {
      // Use "SelectedItem" property value as the BindingContext for this new page.
      await ds.Get<INavigationService>().NavigateAsync(AppPages.Page1, SelectedItem);
   }
}

Then NavigateAsync is called, the service will do several actions:

  1. If the root page is a MasterDetailPage, the app is running on a phone form-factor, and the HideMasterPageOnNavigation page is true, then the MasterDetailPage.IsPresented property is set to false.

  2. It looks up the passed page key object to find the creator delegate. If no registered key is found, then the navigation returns.

  3. It calls the delegate function to create the page.

  4. If a Page value is returned from the delegate, it calls the NavigationPage and performs a navigation. If null was returned, then it simply exits and assumes the app took care of the navigation.