maui
maui copied to clipboard
Navigating backward produces "Pending Navigations still processing" exception
Description
There is no problem when I navigate from master page to detail page. But the reverse produces the following exception.

Steps to Reproduce
Configuration:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
builder.Services
.AddHttpClient<MonkeyService>();
builder.Services
.AddSingleton<MasterViewModel>()
.AddSingleton<MasterPage>();
builder.Services
.AddTransient<DetailViewModel>()
.AddTransient<DetailPage>();
return builder.Build();
}
}
AppShell:
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(DetailPage), typeof(DetailPage));
}
}
Model:
public class Monkey
{
public string Name { get; set; } = default!;
public string Location { get; set; } = default!;
public string Details { get; set; } = default!;
public string Image { get; set; } = default!;
public int Population { get; set; }
public float Latitude { get; set; }
public float Longitude { get; set; }
}
Service:
public class MonkeyService
{
private readonly HttpClient httpClient;
public MonkeyService(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public async ValueTask<IEnumerable<Monkey>?> GetMonkeys()
{
var response = await httpClient.GetAsync("https://www.montemagno.com/monkeys.json");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadFromJsonAsync<IEnumerable<Monkey>>();
}
return Enumerable.Empty<Monkey>();
}
}
View Model:
public partial class MasterViewModel : ObservableObject
{
private readonly MonkeyService service;
public MasterViewModel(MonkeyService service)
{
this.service = service;
}
public ObservableCollection<Monkey> Monkeys { get; } = new();
public async Task ReloadAsync()
{
Monkeys.Clear();
var monkeys = await service.GetMonkeys();
foreach (var monkey in monkeys ?? Enumerable.Empty<Monkey>())
Monkeys.Add(monkey);
}
}
[QueryProperty(nameof(Monkey), nameof(Monkey))]
public partial class DetailViewModel : ObservableObject
{
private readonly MonkeyService service;
public DetailViewModel(MonkeyService service)
{
this.service = service;
}
[ObservableProperty]
Monkey monkey = default!;
}
Pages:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:MauiClient.Models"
xmlns:viewmodels="clr-namespace:MauiClient.ViewModels"
x:Class="MauiClient.Pages.MasterPage"
x:DataType="viewmodels:MasterViewModel"
Title="Master Page"
>
<CollectionView
ItemsSource="{Binding Monkeys}"
SelectionMode="Single"
SelectionChanged="OnSelectionChanged"
>
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:Monkey" >
<VerticalStackLayout>
<Label Text="{Binding Name}" />
</VerticalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
public partial class MasterPage : ContentPage
{
public MasterPage(MasterViewModel model)
{
InitializeComponent();
BindingContext = model;
}
protected override async void OnAppearing()
{
base.OnAppearing();
if (BindingContext is MasterViewModel model)
{
Debug.WriteLine(nameof(OnAppearing));
await model.ReloadAsync();
}
}
private async void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
Debug.WriteLine(nameof(OnSelectionChanged));
var navparam = new Dictionary<string, object?>
{
[nameof(Monkey)] = e.CurrentSelection.FirstOrDefault() as Monkey
};
await Shell.Current.GoToAsync(nameof(DetailPage), navparam);
}
}
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MauiClient.ViewModels"
x:Class="MauiClient.Pages.DetailPage"
x:DataType="viewmodels:DetailViewModel"
Title="Detail Page"
>
<VerticalStackLayout >
<Label Text="{Binding Monkey.Name}" />
<Button Text="Back" Clicked="Back_Clicked"/>
</VerticalStackLayout>
</ContentPage>
public partial class DetailPage : ContentPage
{
public DetailPage(DetailViewModel model)
{
InitializeComponent();
BindingContext = model;
}
private async void Back_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
}
Link to public reproduction project repository
https://github.com/pstricks-fans/MauiClient
Version with bug
7.0 (current)
Last version that worked well
Unknown/Other
Affected platforms
Windows
Affected platform versions
Windows 10 build number: 19044.2251
Did you find any workaround?
Replacing with gesture as follows works.
<VerticalStackLayout.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding GoToDetailCommand,Source={RelativeSource AncestorType={x:Type viewmodels:MasterViewModel}}}"
CommandParameter="{Binding .}"/>
</VerticalStackLayout.GestureRecognizers>
Here are the updated parts:
public partial class MasterViewModel : ObservableObject
{
// Other parts are the same and left for the sake of simplicity!
[RelayCommand]
async Task GoToDetailAsync(Monkey monkey)
{
if (monkey is not null)
{
var navparam = new Dictionary<string, object>
{
[nameof(DetailViewModel.Monkey)] = monkey
};
await Shell.Current.GoToAsync(nameof(DetailPage), navparam);
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage ...>
<CollectionView ItemsSource="{Binding Monkeys}" SelectionMode="None">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:Monkey" >
<VerticalStackLayout>
<VerticalStackLayout.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding GoToDetailCommand,Source={RelativeSource AncestorType={x:Type viewmodels:MasterViewModel}}}"
CommandParameter="{Binding .}"/>
</VerticalStackLayout.GestureRecognizers>
<Label Text="{Binding Name}" />
</VerticalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
Relevant log output
System.InvalidOperationException HResult=0x80131509
Message=Pending Navigations still processing
Source=Microsoft.Maui.Controls StackTrace: at Microsoft.Maui.Controls.ShellSection.Microsoft.Maui.IStackNavigation.RequestNavigation(NavigationRequest eventArgs) at Microsoft.Maui.Controls.Handlers.ShellSectionHandler.SyncNavigationStack(Boolean animated, NavigationRequestedEventArgs e) at Microsoft.Maui.Controls.Handlers.ShellSectionHandler.OnNavigationRequested(Object sender, NavigationRequestedEventArgs e) at Microsoft.Maui.Controls.ShellSection.InvokeNavigationRequest(NavigationRequestedEventArgs args) at Microsoft.Maui.Controls.ShellSection.d__86.MoveNext()
at Microsoft.Maui.Controls.ShellSection.d__70.MoveNext()
at Microsoft.Maui.Dispatching.DispatcherExtensions.<>c__DisplayClass3_0.<b__0>d.MoveNext() at Microsoft.Maui.Dispatching.DispatcherExtensions.<>c__DisplayClass2_0`1.<b__0>d.MoveNext() at Microsoft.Maui.Controls.ShellNavigationManager.d__14.MoveNext() at MauiClient.Pages.DetailPage.<Back_Clicked>d__1.MoveNext() in C:\Projects\MauiClient\MauiClient\Pages\DetailPage.xaml.cs:line 16
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
There's definitely a bug here where Shell should handle this better but I think what's causing your issue is that OnAppearing is firing when you navigate back, which fires reload, which fires selection changed, which fires "GotoAsync" while it's going back.
If you clear the selection before navigating it all works fine
Verified this on Visual Studio Enterprise 17.7.0 Preview 1.0. Repro on Windows 11 with provided Project: MauiClient-main.zip