MAUI Shell 导航崩溃(随机?)(Windows)

问题描述 投票:0回答:1

我创建了一个简单的 MAUI 项目来对内置 MAUI AppShell 导航进行故障排除。

我有一个

MainPage
(绑定到
MainPageViewModel
),其
CollectionView
ItemViewModel
。 我在 XAML 中添加了一个手势行为,以响应双击每个呈现的
ItemViewModel
,这将触发导航到我的第二页:
SelectPage
。 我已经以相对方式完成了导航的路由,因此我可以使用导航栏中的内置返回按钮返回。

我在两个页面之间来回测试此导航,直到它在 App.g.I.cs 生成的文件中出现

Microsoft.UI.Xaml.Controls.Frame.NavigationFailed was unhandled.
未处理的异常。

这可以通过任何一种导航方式发生(转到我的第二页或返回到我的主页)。 此异常在一段时间后发生,但没有特殊模式(例如可能是第 5 次或第 13 次导航)。

(请注意,当使用ScreenToGif在视频上记录此问题时,到目前为止它总是在第二次导航时崩溃?!)

App.g.i.cs 中的第二个调试器中断是关于关闭应用程序之前的

System.Runtime.InteropServices.COMException
异常。

这是我在 GitHub 上的简单项目,用于代码审查。

这是我的视频来说明这个问题

最后显示的堆栈跟踪是:

at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|38_0(Int32 hr)
at ABI.Microsoft.UI.Xaml.Controls.IFrameMethods.Navigate(IObjectReference _obj, Type sourcePageType, Object parameter, NavigationTransitionInfo infoOverride)
at Microsoft.Maui.Platform.StackNavigationManager.NavigateTo(NavigationRequest args)
at Microsoft.Maui.CommandMapper.InvokeCore(String key, IElementHandler viewHandler, IElement virtualView, Object args)
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.OnPushAsync(Page page, Boolean animated)
at Microsoft.Maui.Controls.ShellSection.PushStackOfPages(List`1 pages, Nullable`1 animate)
at Microsoft.Maui.Controls.ShellSection.GoToAsync(ShellNavigationRequest request, ShellRouteParameters queryData, IServiceProvider services, Nullable`1 animate, Boolean isRelativePopping)
at Microsoft.Maui.Controls.ShellNavigationManager.GoToAsync(ShellNavigationParameters shellNavigationParameters, ShellNavigationRequest navigationRequest)
at TestinMAUIPageNavigationPerf.Sources.ViewModels.ItemViewModel.SelectItem()

请注意,当我的第二页根目录中没有元素时

<ContentPage>
它似乎不会崩溃(至少在我测试它的很长一段时间内)。 但仅仅在内部添加一个简单的
<Label Text="TEST!"/>
就会再次崩溃。

编辑 我简化了代码,只在

Button
中有一个
MainPage
来触发导航,在
Label
上有一个
SelectPage
。在相同的情况下,应用程序仍然会崩溃(在 2 个页面之间来回)。

为了参考和满足您的好奇心,这里有这个超级简化版本的视频,但最终仍然崩溃。

c# xaml maui
1个回答
0
投票
有趣!当我克隆您的存储库并在 Windows 机器平台上运行它时,通过添加自测试循环可以轻松重现该错误。基于此,我测试了Rufo爵士改为

AddTransient

的建议。不管怎样,它通常会在大约 5 个周期内引发 
NavigationFailed
 异常。

克隆:orig-with-test-loop

使用循环重现 Bug

public partial class MainPage : ContentPage { . . . protected override async void OnNavigatedTo(NavigatedToEventArgs args) { base.OnNavigatedTo(args); #if SELF_TEST await Task.Delay(AppShell.TestInterval); // After first 'long' setup interval, test at smaller increments. AppShell.TestInterval = TimeSpan.FromSeconds(1); await Shell.Current.GoToAsync(nameof(SelectPage)); Debug.WriteLine($"Count = {_debugCount++}"); #endif } int _debugCount = 1; }
public SelectPage()
{
    InitializeComponent();
}
protected override
    async   // Added for test
    void OnNavigatedTo(NavigatedToEventArgs args)
{
    base.OnNavigatedTo(args);
    Task.Delay(1).Wait(); // < Per original design
    BindingContext = MauiProgram.MainPage?.SelectedItemViewModel;


#if SELF_TEST
    await Task.Delay(AppShell.TestInterval);
    _ = Shell.Current.GoToAsync($"///{nameof(MainPage)}");
#endif
}

Conditional symbol


通过测试循环的修改绑定

基于 SELF_TEST,它提供了一种可靠的方法来重现 bug 并针对它测试更改,我希望在对架构进行尽可能少的更改的情况下进行改进。然而,为了使其通过受限随机测试,我必须做出一些明显不平凡的更改。如果你愿意,你可以尝试一下这些方法,看看它是否能推动你的事情向前发展。它在这里运行得很好;我已经在 Windows 机器上运行了超过 100 次迭代,其中循环重复从

ItemViewModel

 中选择随机 
Items
 并使用它来调用 
SelectItem

克隆:windows-machine-successful-test


变化

最大的变化是将

SelectItem

 命令移至 
MainPageViewModel

public partial class MainPageViewModel : ObservableObject { public static ItemViewModel? SelectedItemViewModel { get; set; } [RelayCommand] private async Task SelectItem(ItemViewModel item) { SelectedItemViewModel = item; try { if (App.Current?.MainPage?.Handler != null) { await Shell.Current.GoToAsync(nameof(SelectPage)); } } catch (Exception e) { Debug.Fail(e.Message); } } public ItemViewModel[] Items { get; } = new[] { new ItemViewModel{ Title = "One" }, new ItemViewModel{ Title = "Two" }, new ItemViewModel{ Title = "Three" }, new ItemViewModel{ Title = "Four" }, new ItemViewModel{ Title = "Five" }, }; }
这样做时,

SelectPage

需要做的就是在
OnAppearing
方法中拉取当前的BC。

public partial class SelectPage : ContentPage { public SelectPage() { InitializeComponent(); } protected override void OnAppearing() { base.OnAppearing(); if(MainPageViewModel.SelectedItemViewModel is ItemViewModel valid) { BindingContext = valid; } } #if SELF_TEST protected override async void OnNavigatedTo(NavigatedToEventArgs args) { base.OnNavigatedTo(args); await Task.Delay(AppShell.TestInterval); if (Handler != null) { // Discard task to keep self-test stack from creeping from recursion _ = Shell.Current.GoToAsync($"//{nameof(MainPage)}"); } } #endif }
这反映了对 

MainPage.xaml 的这些更改:

<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:views="clr-namespace:TestinMAUIPageNavigationPerf.Sources.Views" xmlns:viewmodels="clr-namespace:TestinMAUIPageNavigationPerf.Sources.ViewModels" x:Class="TestinMAUIPageNavigationPerf.Sources.Views.MainPage" x:DataType="viewmodels:MainPageViewModel"> <!--Set BC here with x:Ref="Page"--> <ContentPage.BindingContext> <viewmodels:MainPageViewModel x:Name="Page"/> </ContentPage.BindingContext> <ScrollView> . . . <CollectionView> <CollectionView.ItemTemplate> <DataTemplate x:DataType="viewmodels:ItemViewModel"> <Border> . . . <Border.GestureRecognizers> <TapGestureRecognizer NumberOfTapsRequired="2" Command="{Binding SelectItemCommand, Source={x:Reference Page }}" CommandParameter="{Binding .}"/> </Border.GestureRecognizers> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> . . . </ScrollView> </ContentPage>
MainPage 后面的代码中唯一添加的功能是 SELF_TEST 块。

public partial class MainPage : ContentPage { public MainPage() => InitializeComponent(); new MainPageViewModel BindingContext =>(MainPageViewModel)base.BindingContext; #if SELF_TEST protected override async void OnNavigatedTo(NavigatedToEventArgs args) { base.OnNavigatedTo(args); await Task.Delay(AppShell.TestInterval); // After for optional 'long' setup interval for the first // iteration, followed by test at smaller increments. AppShell.TestInterval = TimeSpan.FromSeconds(1); Debug.WriteLine($"Count = {_debugCount++}"); var randoBC = BindingContext.Items[_rando.Next(BindingContext.Items.Length)]; BindingContext.SelectItemCommand.Execute(randoBC); } int _debugCount = 1; Random _rando = new Random(Seed: 1); #endif }
小改动
    删除了 MainPage、MainPageViewModel 和 SelectPage 的单例注册。
  • 使所有 C'Tors 无参数。
© www.soinside.com 2019 - 2024. All rights reserved.