Prism for Xamarin Forms - Part III: Advanced Navigation
Part III takes a deep dive into how the Prism framework handles advanced app navigation concepts and complex navigation flows.
Join the DZone community and get the full member experience.
Join For Freein the previous post, we’ve expanded a bit on our original sample application by creating a service to interact with the apis offered by the trackseries website and using it to populate some data in the app. in the end, we had a detail page where we can see more info about the selected show. we have understood how prism allows the management of some basic concepts like navigation, page’s lifecycle and dependency injection.
in this post, we’re going to see a couple of more advanced concepts which can be fundamental when it comes to developing a real project using xamarin forms.
did you miss parts i and ii ? click the links to check them out and catch up!
advanced navigation
if you have tested the previous version of the app, you would have noticed some issues with navigation, especially if you have navigated to the detail page of a tv show. for example, if you test the uwp version on a windows 10 pc, you will notice that the back button that is usually available in the top left corner is missing. or on android or ios, the navigation bar which shows the title of the page and the virtual back button is missing, so if your device doesn’t have an actual back button (like an iphone), you don’t have a way to go back home once you are into a show detail page.
if you have some previous experience with xamarin forms you should already have an idea why this problem is happening. every basic page in a xamarin forms app is represented by the contentpage class, but it can be embedded in other kinds of pages to provide more advanced navigation scenarios, like a navigationpage (to provide a navigation ui) or a tabbedpage (to show multiple pages in different tabs). in the sample we’ve created so far, we’ve just created two basic pages (of type contentpage ) and then we’ve simply navigated to them using the navigationservice . we haven’t specified anywhere that the pages should have been actually embedded into a navigationpage to get access to the navigation ui.
to achieve this goal in a plain xamarin forms app, we would have done something like this:
public partial class app : application
{
public app()
{
mainpage = new navigationpage(new mainpage());
}
}
an instance of the mainpage of the application is embedded into a new instance of a navigationpage : from now on, the navigationpage will be the container of each contentpage that the user will see, providing a consistent navigation ui across every page of the app. however, with the prism approach, we can’t recreate the same approach:
public partial class app : prismapplication
{
public app(iplatforminitializer initializer = null) : base(initializer) { }
protected override void oninitialized()
{
initializecomponent();
navigationservice.navigateasync("mainpage");
}
protected override void registertypes()
{
container.registertypefornavigation<mainpage>();
container.registertypefornavigation<detailpage>();
container.registertype<itsapiservice, tsapiservice>();
}
}
we have just registered (in the container) the mainpage and then we have called the navigateasync() method of the navigationservice which accepts only the key that identifies the destination page. we can’t specify, in any way, that this page should be embedded into a navigationpage .
luckily, prism has a smart solution for this problem: deep linking. one of the features offered by prism for xamarin forms is to support complex queries as parameters of the navigateasync() method. for example, we can specify queries like “ mainpage/detailpage?id=1” to navigate directly to the detail page of the app and, at the same time, passing a parameter called id with value 1. this approach is very useful when, for example, you want to link a specific page of your application from another application, a website, or a section of your app.
we can leverage this feature also to achieve the goal of embedding our pages into a navigationpage: first, we need to register the base navigationpage type included in xamarin forms as a type for navigation in the container . then, we can use the query “navigationpage/mainpage” to tell the navigationservice that we need to navigate first to the page identified by the navigationpage key and then to the one identified by the mainpage key. since the navigationpage isn’t actually a real page, but just a container, the end result will be the same we’ve seen in the first sample code: the mainpage (and every consequent page in the navigation flow) will be embedded into a navigationpage.
here is what our new app class looks like:
public partial class app : prismapplication
{
public app(iplatforminitializer initializer = null) : base(initializer) { }
protected override void oninitialized()
{
initializecomponent();
navigationservice.navigateasync("navigationpage/mainpage");
}
protected override void registertypes()
{
container.registertypefornavigation<navigationpage>();
container.registertypefornavigation<mainpage>();
container.registertypefornavigation<detailpage>();
container.registertype<itsapiservice, tsapiservice>();
}
}
thanks to this change we have a proper navigation bar, as you can see in the following screenshot taken from the android version:
another example of complex deep linking is using query parameters. you can use a navigation query like this:
navigationservice.navigateasync("firstpage?id=1&title=first page");
automatically, the destination page will receive (in the navigationparams object of the onnavigatedto() method) two items: one with key id and value 1 and one with key title and value first page.
public void onnavigatedto(navigationparameters parameters)
{
string id = parameters["id"].tostring();
string title = parameters["title"].tostring();
title = $"page with id {id} and title {title}";
}
eventually, you can use this feature to leverage even more complex navigation flows which involve containers for multiple pages.
let’s try to better understand this scenario with a real example. we have already talked about the concept that xamarin forms offers some pages that don't display any actual content, but rather act as a container for other pages. we’ve already seen an example of this concept: the navigationpage type doesn’t display any actual content, but it’s a container to add navigation ui and features to a contentpage . another similar container is tabbedpage , where every child page is displayed in a different tab.
let’s say that we want to improve our tv show application and add two sections to the main page, using a tabbedpage control: the first section will display the list of upcoming shows, so it will be a simple contentpage; the second section will display a list of the available tv shows and will be embedded into a navigationpage, because we want to provide the ability to tap on a show and see more info about the selected show.
this is what our project would look like:
the application has two main sections ( upcomingshowspage and showslistpage ) and a detail page ( detailpage ), each of them with its own viewmodel. the main pages are presented to the user as two sections of a tab control, which is defined in the maintabbedpage.xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<tabbedpage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:prism.mvvm;assembly=prism.forms"
xmlns:views="clr-namespace:prismtest.views;assembly=prismtest"
prism:viewmodellocator.autowireviewmodel="true"
title="main page"
x:class="prismtest.views.maintabbedpage">
<views:upcomingshowspage />
<navigationpage title="shows list">
<x:arguments>
<views:showslistpage />
</x:arguments>
</navigationpage>
</tabbedpage>
the upcomingshowspage is a simple contentpage , while the showslistpsage is embedded into a navigationpage , since the user has the chance to move to the detailpage to see more info about the selected tv show. now let’s say that (as a consequence of an user action) we want to redirect the user to the detail page of a specific tv show. with standard xamarin forms it wouldn’t be a hard task to accomplish, but the real challenge would be to retain the whole navigation stack. we want to bring the user to the detail page, but we also want that when he presses the back button he follows the proper backward navigation flow (so detailpage –> showlistpage ). additionally, everything should be done by keeping the focus in the second tab, since showlistpage is part of a tabbedpage .
sounds complicated, isn’t it? well, here is how it’s easy to achieve this goal with prism:
public partial class app : prismapplication
{
public app(iplatforminitializer initializer = null) : base(initializer) { }
protected override void oninitialized()
{
initializecomponent();
navigationservice.navigateasync("maintabbedpage/navigationpage/showslistpage/detailpage?id=1");
}
protected override void registertypes()
{
container.registertypefornavigation<upcomingshowspage>();
container.registertypefornavigation<showslistpage>();
container.registertypefornavigation<detailpage>();
container.registertypefornavigation<maintabbedpage>();
container.registertypefornavigation<navigationpage>();
}
}
as usual, in the registertypes() method, we have registered every page that composes our application. then we invoke the navigateasync() method of the navigationservice passing the whole path we want to follow: maintabbedpage/navigationpage/showslistpage/detailpage with an additional parameter that identifies the selected tv show, that we can intercept in the onnavigatedto() method of the detailpageviewmodel .
public class detailpageviewmodel : bindablebase, inavigationaware
{
private readonly itsapiservice _tsapiservice;
private serieinfovm _selectedshow;
public serieinfovm selectedshow
{
get { return _selectedshow; }
set { setproperty(ref _selectedshow, value); }
}
public detailpageviewmodel(itsapiservice tsapiservice)
{
_tsapiservice = tsapiservice;
}
public void onnavigatedfrom(navigationparameters parameters)
{
}
public async void onnavigatedto(navigationparameters parameters)
{
int id = convert.toint32(parameters["id"]);
selectedshow = await _tsapiservice.getseriebyid(id);
}
}
thanks to prism, other than achieving the goal of redirecting the user directly to the page we’re interested in, we have also retained the full backward navigation stack. the following images show you what happens when you press the back button:
as you can see from the detail page of the show (the first screenshot) we’ve been properly redirected to the previous page in the stack (the shows list, displayed in the second screenshot), even if we didn’t actually visit it during our navigation flow (since the app was directly loaded in the detail page). additionally, we’ve kept the focus on the second tab ( shows list ) so the user has still the chance, at any time, to move to the first one ( upcoming shows ). pretty cool, isn’t it?
attention : in the current prism implementation dealing with the tabbedpage control has a downside. in our example, as we’ve seen from the screenshot of the project’s structure, the upcoming shows section is represented by a standard page ( upcomingshowspage ) with its own viewmodel ( upcomingshowspageviewmodel ), which implements the inavigationaware interface with the goal to leverage the onnavigatedto() method to load the data (in our case, the list of upcoming shows). as such, the upcomingshowspageviewmodel would look like this:
public class upcomingshowspageviewmodel : bindablebase, inavigationaware
{
private readonly itsapiservice _tsapiservice;
private observablecollection<seriefollowersvm> _topseries;
public observablecollection<seriefollowersvm> topseries
{
get { return _topseries; }
set { setproperty(ref _topseries, value); }
}
public upcomingshowspageviewmodel(itsapiservice tsapiservice)
{
_tsapiservice = tsapiservice;
}
public void onnavigatedfrom(navigationparameters parameters)
{
}
public async void onnavigatedto(navigationparameters parameters)
{
var series = await _tsapiservice.getstatstopseries();
topseries = new observablecollection<seriefollowersvm>(series);
}
}
however, if you tap on the upcoming shows tab you’ll notice that nothing happens and the onnavigatedto() method won’t be triggered. the reason is that the navigation methods implemented by the inavigationaware interface are raised only when you navigate using the prism navigationservice . if the navigation happens without leveraging it (as in this case, where the navigation to the other tab is handled directly by the xamarin forms infrastructure), the onnavigatedto() method in the viewmodel will never be invoked and our data will never be loaded. there’s a solution in the works which involves using a behavior, but it hasn’t been included yet in the current prism version. you can follow the discussion and the proposed solution on github: https://github.com/prismlibrary/prism/issues/650
wrapping up
in this post we’ve learned how to leverage the deep linking featured offered by prism, which allows to handle complex navigation patterns in an easy way, while keeping the proper backward navigation path. in the next post (which will be the last one), we’ll see instead how to use platform-specific code in a xamarin forms application created with prism. you can find all the samples on my github repository: the infoseries one ( https://github.com/qmatteoq/xamarinforms-prism/tree/master/infoseries ) shows you the first approach (simple navigation using a navigationpage), the deepnavigation one ( https://github.com/qmatteoq/xamarinforms-prism/tree/master/deepnavigation ) instead shows you the advanced deep link feature we’ve seen in the second part of the post. happy coding!
Published at DZone with permission of Matteo Pagani, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Extending Java APIs: Add Missing Features Without the Hassle
-
What Is Istio Service Mesh?
-
Working on an Unfamiliar Codebase
-
Microservices Decoded: Unraveling the Benefits, Challenges, and Best Practices for APIs
Comments