Every mobile application needs navigation. Xamarin.Forms Shell offers built-in route based navigation to enable easy navigation to and from pages in your application. Additionally, since it is based on a route schema, you can navigate using absolute or relative routes that will even inflate a full back stack. In most cases, relative route navigation is best as you are pushing a unique page onto the stack. Let’s say you are on a monkeys page and wanted to navigate to the details. You would use a simple relative route to navigate: await Shell.Current.GoToAsync(“monkeydetails”);
Now, to build up a navigation stack for deep linking you can use absolute routes. This will take you to the monkeys route and then push the details page. await Shell.Current.GoToAsync(“//monkeys/details”); This is helpful. However, in both cases it would be great is if you could pass in data to the details page to tell it to display specific monkeys details. Read on for a full walkthrough of how to pass data from one page to another.
Passing dData What happens when you want to pass data from one page to another? Well that is where Shell’s built in
Query Parameters come in. It enables you to seamlessly pass data around your application. Let’s walk through a simple app that increase the count on the page and passes that data to the next page when the app navigates: Look at the code to setup the first page. It is a relatively simple user interface with a few buttons and label to display the count:
public int Count { get; set; }
void ButtonClick_Clicked(object sender, EventArgs e)
{
Count++;
OnPropertyChanged(nameof(Count));
}
Navigating To Page2 Now, let’s say that we want to navigate to the second page, in this case
Page2
. We would first register our route in our Shell: Routing.RegisterRoute(nameof(Page2), typeof(Page2)); Then we can navigate to it using
GoToAsync
on the current Shell. await Shell.Current.GoToAsync(nameof(Page2)); We want to pass the current count, so we will use a query parameter on our path. This follows standard Uri routes such as
path?param1=hello¶m2=world
. So, to pass the count we will navigate passing a parameter named count. Any data that is passed is automatically encoded for you. async void ButtonNavigate_Clicked(object sender, EventArgs e) { await Shell.Current.GoToAsync($”{nameof(Page2)}?Count={Count}”); }
Parsing Parameters Now that we have passed the count using the parameter, we must receive it on the second page. You can do this either in the code behind of the page that you are navigating to, or to the
BindingContext
of the page such as a view model. The first thing to do is to add a public property that Xamarin.Forms Shell will call the set
on. Here, we will create another Count property that is a string
and when we set the value we will call UnescapeDataString
as a best practice. string count = “”; public string Count { get => count; set { count = Uri.UnescapeDataString(value ?? string.Empty); OnPropertyChanged(); } } Lastly, add a
QueryProperty
that tells Xamarin.Forms Shell the property to set the parameter name to parse. Here, we will set the Count
property for the count parameter that we passed. [QueryProperty(nameof(Count), nameof(Count))] public partial class Page2 : ContentPage { //… } In this example, we only passed one parameter. However, you can add as many
QueryProperty
attributes to the top of your class as you would like.
Deep Linking Combining route based navigation with query properties can be extremely powerful when creating apps that deep link from the web or custom data schemes. For example in the application
Island Tracker, a custom data scheme is used to create a link that the app can use to send friend requests. Here is an example of the deep link that a user would click: acislandtracker://friends/invite?id=a3be899c-41df-4ce6-8ca7-26b0642efcde&name=James When it is clicked, Xamarin.Forms will activate the app using the
AppLink API. protected override async void OnAppLinkRequestReceived(Uri uri) { await Shell.Current.GoToAsync($”//{uri.Host}/{uri.PathAndQuery}”); } From there, Shell will ensure the
friend page is navigated to and then it will navigate to the invite page. If navigation to the friend page first was not the intent, simply pass in the full uri
to Shell. It takes care of the rest to open the invite page.
Learn More To learn more about navigation with Xamarin.Forms Shell and more information about using
QueryProperty
attributes, browse through our full documentation on Shell navigation. You can also grab the source code for this sample on GitHub. Want more Xamarin.Forms? Checkout our Xamarin.Forms 101 series on Channel 9.
Currently there is a limitation on the overall size of the uri that can be used for navigation (I believe its a 2048 character limit). This limits the size of objects you can serialize and send via the query (if the overall uri size to too large, the data passed in the query is truncated and cant be deserialized).
There are various approaches you can use to get around this. You could push a page using...
Looks like using the query parameter is good for passing a simple string but I don’t see how it can be used to pass a Model object. For that shouldn’t we continue to use what we’ve always used, namely (and as Rikudou referred to) Navigation.PushAsync(new Pagename(object)); ?
I’m answering my own question: The Shell template app uses Navigation.PushAsync(new Pagename(object));
Doesn’t this sort of negate view models and dependency injection, and all those good practices you have been preaching all these years? Can you mix and match the two? And isn’t it a bit of a kicker that you still have to unescape the strings?
I agree. We have used an injected NavigationManager service to abstract the navigation, provide easier access to this like current/previous page, current and previous navigation stack, and improve data passing, which also allows us to maintain the MVVM framework.
It's early days with Shell, but I'm not currently a fan of the approach and I think its really a solution looking for a problem. Maybe if it adds in things like Tab Templates, full NavBar templates,...
I had the same thought.
Reminds me of the
Navigation.PushAsync(new Pagename(object));