Hi friends, here we are again, my first blog post in 2020. Today we will talk about how to navigate to a page and wait results. This can be really useful by making our code easier to read, as compared to using Xamarin Forms’ Messaging Center each time a page needs to send back results to its caller.

The Problem

In Xamarin Forms, you can navigate asynchronously to a page or a modal. With the Xamarin Forms Shell, you can perform URL navigation and even pass parameters to the page or the page’s View Model. But once you navigate to this page or modal and it completes the processing for which it ways called. How do you return directly a result to the underlying layer which called the page ?. As of when I’m writing this post to my knowledge, the most common way to receive a result back from a page which just disappeared is by using Xamarin Forms’ Messaging Center. Though the messaging center works well, The code you write to register to various events might be hard to read.

Simple Solution

To resolve this, we will just leverage one of C#’s awesome features Tasks. Bellow, we will implement a simple navigation service. This service will wait for the page’s OnDisappearing method to be called. Then return a result gotten from the data input by the user on that page. This sample is done with MVVM pattern, but for simplicity, you will find code behind the view. So, how to we navigate to a page and wait results in Xamarin Forms.

You can download the source code here.

If you like this post, don’t hesitate to follow me on Twitter or Github and subscribe to this page’s notifications to stay updated with new articles or like my page on Facebook.

First, we create a base page with a generic type <T>. Which contains the event fired when the page disappears. This event is made of an action delegate which has as parameter the datatype T.

NB: I chose to make the page responsible of returning the data and calling the event because of simplicity. You could implement this in your View Model to make your app abide more to the MVVM design pattern.

public class BasePage<T> : ContentPage { public event Action<T> PageDisapearing; protected T _navigationResut; public BasePage() { } protected override void OnDisappearing() { PageDisapearing?.Invoke(_navigationResut); if (PageDisapearing != null) { foreach (var @delegate in PageDisapearing.GetInvocationList()) { PageDisapearing -= @delegate as Action<T>; } } base.OnDisappearing(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class BasePage < T > : ContentPage { public event Action < T > PageDisapearing ; protected T _navigationResut ; public BasePage ( ) { } protected override void OnDisappearing ( ) { PageDisapearing ? . Invoke ( _navigationResut ) ; if ( PageDisapearing != null ) { foreach ( var @ delegate in PageDisapearing . GetInvocationList ( ) ) { PageDisapearing -= @ delegate as Action < T > ; } } base . OnDisappearing ( ) ; } }

Every page which will have to navigate to and wait results, should implement this base page.

Now in the navigation service, we create an instance of the page we want to display. Subscribe to its “PageDisapearing” event. And use the “TaskCompletionSource” to wait for the page to finish what it was called for.

public async Task<T> NavigateToModal<T>(string modalName) { var source = new TaskCompletionSource<T>(); if (modalName == nameof(NewItemPage)) { var page = new NewItemPage(); page.PageDisapearing += (result) => { var res = (T)Convert.ChangeType(result, typeof(T)); source.SetResult(res); }; await App.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page)); } return await source.Task; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public async Task < T > NavigateToModal < T > ( string modalName ) { var source = new TaskCompletionSource < T > ( ) ; if ( modalName == nameof ( NewItemPage ) ) { var page = new NewItemPage ( ) ; page . PageDisapearing += ( result ) = > { var res = ( T ) Convert . ChangeType ( result , typeof ( T ) ) ; source . SetResult ( res ) ; } ; await App . Current . MainPage . Navigation . PushModalAsync ( new NavigationPage ( page ) ) ; } return await source . Task ; }

Using Our Navigation Service

Now, here is how you should use this service. Below, we call the create items page which will allow the user to create an item and return the new item when creation completes.

async Task ExecuteAddItemCommand() { var item = await new SimpleNavigationService().NavigateToModal<Item>(nameof(NewItemPage)); Items.Add(item); await DataStore.AddItemAsync(item); } 1 2 3 4 5 6 async Task ExecuteAddItemCommand ( ) { var item = await new SimpleNavigationService ( ) . NavigateToModal < Item > ( nameof ( NewItemPage ) ) ; Items . Add ( item ) ; await DataStore . AddItemAsync ( item ) ; }

Conclusion

Above, you have a complete solution that permits you to navigate to a page and wait results. You can leverage this to make your Xamarin Forms code more readable. Note that, this way of using the TaskCompletionSource can be leveraged in several other scenarios. Specifically scenarios where you want your code to wait for a certain work (which does not depend on your code) to complete and return before continuing your code execution.

Like this: Like Loading...

Follow me on social media and stay updated