Skip to content

Instantly share code, notes, and snippets.

@Axemasta
Created December 1, 2021 10:19
Show Gist options
  • Select an option

  • Save Axemasta/0a08ca435d0c10a42efc34de2ce8f9f0 to your computer and use it in GitHub Desktop.

Select an option

Save Axemasta/0a08ca435d0c10a42efc34de2ce8f9f0 to your computer and use it in GitHub Desktop.
INavigationService Extensions for thread safe + error handled navigation
using System;
using System.Threading.Tasks;
using Prism;
using Prism.Navigation;
using Xamarin.Forms;
using Xamarin.Essentials.Interfaces;
using Xamarin.Essentials.Implementation;
public static class NavigationServiceExtensions
{
public static IMainThread ImplementedInstance { get; } = new MainThreadImplementation();
public static async Task NavigateOnMainThread(this INavigationService navigationService, Func<Task<INavigationResult>> action, IMainThread mainThread)
{
INavigationResult result = null;
if (mainThread is null)
{
mainThread = ImplementedInstance;
}
if (mainThread.IsMainThread)
{
result = await action.Invoke();
}
else
{
result = await mainThread.InvokeOnMainThreadAsync(action);
}
navigationService.HandleNavigationResult(result);
}
public static async Task TryNavigateAsync(this INavigationService navigationService, Uri uri,
INavigationParameters parameters = null,
IMainThread mainThread = null)
{
Func<Task<INavigationResult>> navigation = () => navigationService.NavigateAsync(uri, parameters);
await NavigateOnMainThread(navigationService, navigation, mainThread);
}
public static async Task TryNavigateModallyAsync(this INavigationService navigationService, Uri uri,
INavigationParameters parameters = null,
IMainThread mainThread = null)
{
Func<Task<INavigationResult>> navigation = () => navigationService.NavigateAsync(uri, parameters, true, true);
await NavigateOnMainThread(navigationService, navigation, mainThread);
}
public static async Task TryNavigateAsync(this INavigationService navigationService, string path,
INavigationParameters parameters = null,
IMainThread mainThread = null)
{
Func<Task<INavigationResult>> navigation = () => navigationService.NavigateAsync(path, parameters);
await NavigateOnMainThread(navigationService, navigation, mainThread);
}
public static async Task TryNavigateModallyAsync(this INavigationService navigationService, string path,
INavigationParameters parameters = null,
IMainThread mainThread = null)
{
Func<Task<INavigationResult>> navigation = () => navigationService.NavigateAsync(path, parameters, true, true);
await NavigateOnMainThread(navigationService, navigation, mainThread);
}
public static async Task TryNavigateBackAsync(this INavigationService navigationService,
INavigationParameters parameters = null,
IMainThread mainThread = null)
{
Func<Task<INavigationResult>> navigation = () => navigationService.GoBackAsync(parameters);
await NavigateOnMainThread(navigationService, navigation, mainThread);
}
public static async Task TryClearPopupStackAsync(this INavigationService navigationService,
IMainThread mainThread = null)
{
Func<Task<INavigationResult>> navigation = () => navigationService.ClearPopupStackAsync();
await NavigateOnMainThread(navigationService, navigation, mainThread);
}
private static async void HandleNavigationResult(this INavigationService navigationService,
INavigationResult navigationResult)
{
if (navigationResult is null) // This case should only occur in incorrectly setup unit tests
return;
if (!navigationResult.Success)
{
Exception ex = new InvalidNavigationException();
if (navigationResult.Exception != null)
ex = navigationResult.Exception;
var p = new NavigationParameters()
{
{ ParameterConstants.ExceptionError, ex }
};
var nav = await navigationService.NavigateAsync("NavigationErrorPage", p);
if (!nav.Success)
{
SetMainPageFromException(ex);
}
}
}
private static void SetMainPageFromException(Exception ex)
{
var layout = new StackLayout
{
Padding = new Thickness(40)
};
layout.Children.Add(new Label
{
Text = ex?.GetType()?.Name ?? "Unknown Error encountered",
FontAttributes = FontAttributes.Bold,
HorizontalOptions = LayoutOptions.Center
});
layout.Children.Add(new ScrollView
{
Content = new Label
{
Text = $"{ex}",
LineBreakMode = LineBreakMode.WordWrap
}
});
PrismApplicationBase.Current.MainPage = new ContentPage
{
Content = layout
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment