I have a small .NET MAUI Application with about 18 pages. I was creating all of my routing on the code behind of the AppShell. Like this:
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(LogInPage), typeof(LogInPage));
Routing.RegisterRoute(nameof(SignUpPage), typeof(SignUpPage));
Routing.RegisterRoute(nameof(PasswordRecoveryPage), typeof(PasswordRecoveryPage));
Routing.RegisterRoute(nameof(ModalitiesPage), typeof(ModalitiesPage));
Routing.RegisterRoute(nameof(UserPage), typeof(UserPage));
Routing.RegisterRoute(nameof(HomePage), typeof(HomePage));
Routing.RegisterRoute(nameof(ConversationsPage), typeof(ConversationsPage));
Routing.RegisterRoute(nameof(BookingsPage), typeof(BookingsPage));
Routing.RegisterRoute(nameof(ProfessionalPage), typeof(ProfessionalPage));
Routing.RegisterRoute(nameof(AddPaymentInfoPage), typeof(AddPaymentInfoPage));
Routing.RegisterRoute(nameof(AddAddressPage), typeof(AddAddressPage));
Routing.RegisterRoute(nameof(AddReviewPage), typeof(AddReviewPage));
Routing.RegisterRoute(nameof(PlaygroundPage), typeof(PlaygroundPage));
Routing.RegisterRoute(nameof(FavoritesPage), typeof(FavoritesPage));
Routing.RegisterRoute(nameof(PostLoginPage), typeof(PostLoginPage));
}
However due to having to also register Tabs and a TabBar in the AppShell.xaml, and following some examples I've seen online I've opted for registering the routes in the XAML. I removed the registration in the code-behind and added via XAML. This is my AppShell.xaml as it stands:
<Shell
x:Class="Xen.Presentation.Mobile.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Xen.Presentation.Mobile"
xmlns:views="clr-namespace:Xen.Presentation.Mobile.Views"
Shell.FlyoutBehavior="Disabled">
<ShellContent
Title="Onboarding"
ContentTemplate="{DataTemplate views:OnboardingPage}"
Route="OnboardingPage" />
<ShellContent
Title="LogIn"
ContentTemplate="{DataTemplate views:LogInPage}"
Route="LogInPage" />
<TabBar Route="Main">
<Tab
Title="Inicio"
Icon="home.png"
Route="HomePage">
<ShellContent Title="Home" ContentTemplate="{DataTemplate views:HomePage}" />
</Tab>
<Tab
Title="Favoritos"
Icon="bookings.png"
Route="FavoritesPage">
<ShellContent Title="Favorites" ContentTemplate="{DataTemplate views:FavoritesPage}" />
</Tab>
<Tab
Title="Conversas"
Icon="chat.png"
Route="ConversationsPage">
<ShellContent Title="Conversas" ContentTemplate="{DataTemplate views:ConversationsPage}" />
</Tab>
<Tab
Title="Modalidades"
Icon="modalities.png"
Route="ModalitiesPage">
<ShellContent Title="Modalidades" ContentTemplate="{DataTemplate views:ModalitiesPage}" />
</Tab>
<Tab
Title="Perfil"
Icon="user.png"
Route="ProfileTab">
<ShellContent
Title="Perfil"
ContentTemplate="{DataTemplate views:UserPage}"
Route="UserPage" />
</Tab>
</TabBar>
<ShellContent
Title="SignUpPage"
ContentTemplate="{DataTemplate views:SignUpPage}"
Route="SignUpPage" />
<ShellContent
Title="BookingsPage"
ContentTemplate="{DataTemplate views:BookingsPage}"
Route="BookingsPage" />
<ShellContent
Title="PlaygroundPage"
ContentTemplate="{DataTemplate views:PlaygroundPage}"
Route="PlaygroundPage" />
<ShellContent
Title="FavoritesPage"
ContentTemplate="{DataTemplate views:FavoritesPage}"
Route="FavoritesPage" />
<ShellContent
Title="Professional"
ContentTemplate="{DataTemplate views:ProfessionalPage}"
Route="ProfessionalPage" />
<ShellContent
Title="AddPaymenInfo"
ContentTemplate="{DataTemplate views:AddPaymentInfoPage}"
Route="AddPaymentInfoPage" />
<ShellContent
Title="AddAddress"
ContentTemplate="{DataTemplate views:AddAddressPage}"
Route="AddAddressPage" />
<ShellContent
Title="AddReview"
ContentTemplate="{DataTemplate views:AddReviewPage}"
Route="AddReviewPage" />
<ShellContent
Title="PostReview"
ContentTemplate="{DataTemplate views:PostReviewPage}"
Route="PostReviewPage" />
<ShellContent
Title="PostLogin"
ContentTemplate="{DataTemplate views:PostLoginPage}"
Route="PostLoginPage" />
</Shell>
I don't have any issues when using the TabBar for navigation after login. However some code that used to work now doesn't. For instance, in my UserPageViewModel I have some commands that navigate to other pages. This is the some of the code that now doesn't work anymore:
private async Task OnAddAddress()
{
await Shell.Current.GoToAsync(nameof(AddAddressPage));
}
private async Task OnAddPaymentInfo()
{
await Shell.Current.GoToAsync(nameof(AddPaymentInfoPage));
}
Now when I run any of these methods I get this exception:
System.Exception Message=Relative routing to shell elements is currently not supported. Try prefixing your uri with ///: ///AddAddressPage
If I do like it tells and run this code:
await Shell.Current.GoToAsync("///AddAddressPage");
Or even:
await Shell.Current.GoToAsync("//AddAddressPage");
Then I go to the AddAddressPage, but it seems like it clears the whole navigation stack and when I go back, I go back to the OS and the app goes to the background. Which makes it not a viable option to me.
After changing the routing to XAML I also get error when a page has parameters.For example, from my HomePage when I click on a professional on the list I was navigating to the ProfessionalPage, however after changing the routes to the XAML now when I run this code:
private static async void OnProfessionalTapped(ProfessionalPreview item)
{
var parameters = new Dictionary<string, object>
{
{ "ProfessionalId", item.Id }
};
// Using // to open the page even though I need to go back, just to test
await Shell.Current.GoToAsync($"//{nameof(ProfessionalPage)}", parameters);
}
I get this error:
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Xen.Presentation.Mobile.Models.Professional' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path 'content', line 1, position 12.'
With this call stack:
Is there something wrong with my AppShell.xaml? Should I register the pages on the code-behind? Why?
I read the docs on the Shell navigation, including the part on relative routes, but I still can't figure it out. Also watched some videos:
And looked at some samples, but when it comes to shell it seems like most samples are focused on using Flyouts.
