.NET7 MAUI加载页导航抛出Pending Navigations异常(Windows端)
问题场景
基于.NET 7 MAUI实现登录流程,包含Dashboard页、Login页,以及作为初始路由的无布局Loading页:
- Loading页在AppShell定义中排首位,启动时优先加载
- 在Loading页的
OnNavigatedTo方法中,根据用户本地是否保存令牌,决定重定向至Login页或Dashboard页
Android模拟器运行正常,但Windows应用抛出Pending Navigations still processing异常。临时添加await Task.Delay(1);可规避问题,但属于临时 workaround,需要更合理的解决方案。
相关代码
AppShell 标记代码:
<ShellContent Shell.FlyoutBehavior="Disabled" FlyoutItemIsVisible="False" Title="Loading" ContentTemplate="{DataTemplate layout:LoadingPage}" Route="{x:Static clientApp:Routes.LoadingPage}" /> <ShellContent Shell.FlyoutBehavior="Disabled" FlyoutItemIsVisible="False" Title="Login" ContentTemplate="{DataTemplate account:LoginPage}" Route="{x:Static clientApp:Routes.LoginPage}" /> <FlyoutItem FlyoutDisplayOptions="AsMultipleItems"> <ShellContent Title="Dashboard" ContentTemplate="{DataTemplate dashboard:DashboardPage}" Route="{x:Static clientApp:Routes.DashboardPage}" /> </FlyoutItem>
LoadingPage 问题代码:
public partial class LoadingPage : ContentPage { public LoadingPage() { InitializeComponent(); BindingContext = viewModel; } protected async override void OnNavigatedTo(NavigatedToEventArgs args) { base.OnNavigatedTo(args); // await Task.Delay(1); 取消注释可临时解决问题 await Shell.Current.GoToAsync($"//{Routes.LoginPage}"); } }
解决方案
方案1:通过Dispatcher延迟导航至UI线程空闲时机
Windows端MAUI Shell对导航时序的校验更严格,OnNavigatedTo触发时,当前页面的初始导航尚未完全完成。使用Dispatcher.DispatchAsync将导航操作排入UI队列的后续执行,避开当前pending的导航流程:
protected override void OnNavigatedTo(NavigatedToEventArgs args) { base.OnNavigatedTo(args); // 用Dispatcher将导航延后到当前导航完成后执行 _ = Dispatcher.DispatchAsync(async () => { bool hasToken = /* 你的令牌检查逻辑 */; string targetRoute = hasToken ? $"//{Routes.DashboardPage}" : $"//{Routes.LoginPage}"; await Shell.Current.GoToAsync(targetRoute); }); }
方案2:将初始导航逻辑移至更高层级的生命周期事件
把令牌校验和导航逻辑从LoadingPage的OnNavigatedTo中移出,放到App.OnStart或Shell的Navigated事件中,确保初始导航流程完全结束后再执行跳转:
方式A:在App.xaml.cs的OnStart中处理
protected override async void OnStart() { base.OnStart(); bool hasToken = /* 令牌检查逻辑 */; string targetRoute = hasToken ? $"//{Routes.DashboardPage}" : $"//{Routes.LoginPage}"; await Shell.Current.GoToAsync(targetRoute); }
方式B:在AppShell的Navigated事件中处理
public AppShell() { InitializeComponent(); Navigated += async (sender, e) => { // 仅当LoadingPage导航完成后执行跳转 if (e.SourcePageType == typeof(LoadingPage)) { bool hasToken = /* 令牌检查逻辑 */; string targetRoute = hasToken ? $"//{Routes.DashboardPage}" : $"//{Routes.LoginPage}"; await GoToAsync(targetRoute); } }; }
方案3:禁用LoadingPage的导航缓存(配合方案1使用)
在AppShell的LoadingPage对应的ShellContent中添加NavigationCacheMode="Disabled",避免页面缓存导致的重复导航冲突:
<ShellContent Shell.FlyoutBehavior="Disabled" FlyoutItemIsVisible="False" Title="Loading" ContentTemplate="{DataTemplate layout:LoadingPage}" Route="{x:Static clientApp:Routes.LoadingPage}" NavigationCacheMode="Disabled" />
原理说明
Windows平台MAUI Shell的导航状态管理比Android更严谨,当页面的OnNavigatedTo方法触发时,Shell内部的初始导航流程仍处于pending状态,此时直接发起新导航会触发冲突检查,抛出异常。通过Dispatcher延迟执行或移至更高层级生命周期事件,都是为了确保当前导航完全结束后再执行后续跳转,从根源上避免冲突。
内容的提问来源于stack exchange,提问作者Miloš

