Getting Started Part 4 - Navigating Between Pages Using the Navigation Service

Introduction

In the previous article you looked at enabling communication between components in your app using the Messenger class. In this article, you look at enabling page navigation using the routing service in conjunction with the navigation service.

The code presented herein is located in Sample001 in the Samples repository

The page navigation APIs differ significantly across platforms. Some platforms, like the UWP, allow you to navigate to a page using its Type. In WPF, you navigate to a Page instance. While others use a completely different scheme, such as Android and its use of Intents.

To ease these differences, Calcium employs a routing system, that allows you to register a string URL with an associated Action. When the navigation service receives a request to navigate to a particular URL, the action associated with that URL is invoked.

NOTE: The URL can, in fact, be any string, and serves merely as a key to look up the associated Action.

Understanding the Routing Service

Calcium does not require bootstrapping. Various default IoC container registrations are performed automatically. However, if you intend to use Calcium’s navigation service for anything besides back navigation, you need to configure its routes.

In the sample, you can find a class named Bootstrapper in each platform project. Its sole purpose is to configure the routing service.

Each of the Bootstrapper classes contain a Run method, which is called when the application launches. The Bootstrapper in the UWP sample project, retrieves the IRoutingService from the IoC container and registers the path “/Page2” (contained within the Routes class) with an Action to call the Bootstrapper class’s Navigate method. See Listing 1.

Listing 1. UWP Sample Bootstrapper class.

public void Run()
{
	/* This method should be called when your app starts. 
	 * The IoC container knows about several default types, 
	 * that's why no type registrations are necessary. */
	var routingService = Dependency.Resolve<IRoutingService>();

	/* When the navigation service receives a request for "/Page2", 
	 * it uses the routing service to look up the path 
	 * and calls Navigate<Page2>(). */
	routingService.RegisterPath(Routes.Page2, Navigate<Page2>);
}

The generic Navigate method calls the non-generic navigate method, which then retrieves the Frame object from the Window, and calls its Navigate method with the page Type as a parameter. See Listing 2.

NOTE: You don’t have set up the Bootstrapper like this. It’s merely a guide. Remember, the routing service accepts an Action, so the flexibility is there to do whatever you like.

Listing 2. UWP Bootstrapper Navigate methods

void Navigate<TPage>()
{
	Navigate(typeof(TPage));
}

void Navigate(Type pageType)
{
	/* Navigation in UWP is achieved using the root Frame. */
	var frame = (Frame)Window.Current.Content;
	frame.Navigate(pageType);
}

The sample Bootstrapper for WPF looks much the same as the UWP Bootstrapper, apart from its Navigate method. See Listing 3.

The Calcium INavigationService implementation for WPF contains a convenient helper method, which locates the built-in System.Navigation.NavigationService from current Frame or Window. Since we know the Bootstrapper is running on WPF we can safely cast the INavigationService to its concrete implementation NavigationService.

The WPF Bootstrapper uses the Dependency class to build-up the new page, which is then passed to the built-in NavigationService object.

Listing 3. WPF Sample Bootstrapper Navigate method

void Navigate(Type pageType)
{
	var navigationService = (NavigationService)Dependency.Resolve<INavigationService>();
	var page = Dependency.ResolveWithType(pageType);

	/* The platform specific implementation of INavigationService 
	 * has a Navigate(object) method that we can use navigate. 
	 * It automatically resolves the root Frame or NavigationPage. */
	navigationService.Navigate(page);
}

The Bootstrapper class in the Sample Android app, works much the same as the other platforms. The main difference, however, is that it makes use of Android Intents. The Bootstrapper class’s Run method retrieves the IRoutingService instance and registers a Page 2 route with an associated call to the LaunchActivity method, as shown:

routingService.RegisterPath(Routes.Page2, () => LaunchActivity<Page2Activity>(1));

Navigating in Xamarin Android requires an Activity or Context object. Because there isn’t a global handle to the current Activity, Calcium requires that when an Activity becomes active, it is registered with the IoC container.

The non-generic LaunchActivity method of the Android Sample Bootstrapper retrieves the current Activity from the IoC container. See Listing 4. It then creates an Intent for the new activity. The new activity is started using the Intent object and a request code. There will be more on request codes in a later article.

Listing 4. Android Sample Bootstrapper LaunchActivity methods

void LaunchActivity<TActivity>(int requestCode)
{
	LaunchActivity(typeof(TActivity), requestCode);
}

void LaunchActivity(Type activityType, int requestCode)
{
	var activity = Dependency.Resolve<Activity>();
			
	/* Launch an intent for the activity. */
	Intent intent = new Intent(activity, activityType);
	activity.StartActivityForResult(intent, requestCode);
}

Returning to the Page1ViewModel, you see that the ActionCommand named NavigateToPage2Command calls the view-models NavigateToPage2 method, as shown:

ActionCommand navigateToPage2Command;

public ICommand NavigateToPage2Command => navigateToPage2Command
	?? (navigateToPage2Command = new ActionCommand(NavigateToPage2));

The NavigateToPage2 method uses the navigation service to navigate to the URL defined in Routes.Page2 (“/Page2”). See Listing 5. The navigation service then asks the routing service if there is a route registered with the path “/Page2”, and as there is, invokes the Action associated with the URL; navigating to the second page.

Listing 5. Page1ViewModel NavigateToPage2 method

void NavigateToPage2(object arg)
{
	navigationService.Navigate(Routes.Page2);
}

So, to recap: Use the IRoutingService to associate URLs with Actions that perform the actual navigation. Use the INavigationService to navigate to the URLs.

Back Navigation via a View-Model Command

There is a button on each of the platform Page1 views that is bound to the NavigateToPage2Command. When activating the button, the UWP/WPF/Android apps navigate to Page 2. On Page 2 there is a button that is bound to the Page2ViewModel class’s NavigateBackCommand. When the command executes, the INavigationService is called upon to perform a back navigation, like so:

void NavigateBack(object arg)
{
	navigationService.GoBack();
}

Each platform specific implementation of INavigationService knows how to perform a back navigation for its particular platform.

Conclusion

In this article, you look at enabling page navigation using the routing service in conjunction with the navigation service.