InvalidCastException在使用MAUI Shell导航并将对象传递给页面时发生。

huangapple go评论87阅读模式
英文:

InvalidCastException when using MAUI Shell navigation and passing an object to page

问题

当我尝试在不传递参数的情况下导航到页面时,一切都按预期工作。
一旦我将一个Dictionary传递给方法,我就会收到一个奇怪的异常,内容为:"应用程序处于中断模式,您的应用程序已进入中断状态,但没有代码可显示,因为所有线程都在执行外部代码(通常是系统或框架代码)"。

执行导航的命令如下:

[RelayCommand]
async Task OpenEntry(Training training)
{
    // 这将导航到页面
    // await Shell.Current.GoToAsync(nameof(DetailPage));
    // 这不会导航到页面,而是抛出异常:System.InvalidCastException: 'Object must implement IConvertible.'
    await Shell.Current.GoToAsync(
        nameof(DetailPage), 
        true, 
        new Dictionary<string, object>() 
        { 
            { "DetailTraining", (Training)training } 
        });
}

我想要导航到的页面的ViewModel如下:

[QueryProperty(nameof(DetailedTraining),"DetailTraining")]
public partial class DetailViewModel : ObservableObject
{
    [ObservableProperty]
    Training detailedTraining;

    public DetailViewModel()
    {}
}

异常的调用堆栈如下:

0xFFFFFFFFFFFFFFFF in Android.Runtime.JNIEnv.monodroid_debugger_unhandled_exception	C#
0x1A in Android.Runtime.JNINativeWrapper._unhandled_exception at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:12,5	C#
0x1D in Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:23,26	C#
0x17 in System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw	C#
0x6 in System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0	C#
0xC in Android.App.SyncContext. at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.App/SyncContext.cs:36,19	C#
0xE in Java.Lang.Thread.RunnableImplementor.Run at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Lang/Thread.cs:36,6	C#
0x8 in Java.Lang.IRunnableInvoker.n_Run at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net7.0/android-33/mcw/Java.Lang.IRunnable.cs:84,4	C#
0x8 in Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:22,5	C#

正如我所说,我认为我已经正确设置了路由和依赖项,因为简单的导航(不传递对象)工作正常。

我还尝试将字符串传递到页面,代码如下:

await Shell.Current.GoToAsync($"{nameof(DetailPage)}?Text={training.Name}");

然后我调整了QueryProperty:

[QueryProperty("Text","Text")]

我还在ViewModel中添加了:

[ObservableProperty] text;

当我尝试以这种方式传递字符串时,一切都按预期工作。

但一旦我尝试使用以下重载:

public Task GoToAsync(ShellNavigationState state, IDictionary<string, object> parameters);

应用程序会崩溃,并且Visual Studio 2022会显示"您的应用程序处于中断模式"屏幕以及一个异常弹出窗口,显示"System.InvalidCastException: 'Object must implement IConvertible.'"。

所有这些都发生在Android模拟器上运行时。
当我尝试在Windows上运行时,它会在"App.g.i.cs"(自动生成的文件)中断开,如下所示:

#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
    UnhandledException += (sender, e) =>
    {
        if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
    };
#endif

我还尝试了此帖子中的@ToolmakerSteve的解决方法:
https://stackoverflow.com/questions/75821516/maui-shell-navigation-fails-rare-reproduction
但这只会使Windows版本立即显示"System.InvalidCastException"弹出窗口,而不是在自动生成的文件中中断。

谢谢您的提前帮助!

英文:

When I try navigating to the page without passing an argument, everything works as expected.
As soon as I pass a Dictionary into the Method I get a weird exception with
"The application is in break mode
Your app has entered a break state, but there is no code to show because all threads were executing external code (typically system or framework code)."

Command that executes the navigation:

[RelayCommand]
async Task OpenEntry(Training training)
{
    //This navigates to page
    //await Shell.Current.GoToAsync(nameof(DetailPage));
    //This doesnt navigate to page and throws: System.InvalidCastException: 'Object must implement IConvertible.'
    await Shell.Current.GoToAsync(
        nameof(DetailPage), 
        true, 
        new Dictionary<string, object>() 
        { 
            { "DetailTraining", (Training)training } 
        });
}

Viewmodel of page I want to navigate to:

[QueryProperty(nameof(DetailedTraining),"DetailTraining")]
public partial class DetailViewModel : ObservableObject
{
    [ObservableProperty]
    Training detailedTraining;

    public DetailViewModel()
    {}
}

Callstack of exception:

0xFFFFFFFFFFFFFFFF in Android.Runtime.JNIEnv.monodroid_debugger_unhandled_exception	C#
0x1A in Android.Runtime.JNINativeWrapper._unhandled_exception at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:12,5	C#
0x1D in Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:23,26	C#
0x17 in System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw	C#
0x6 in System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0	C#
0xC in Android.App.SyncContext. at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.App/SyncContext.cs:36,19	C#
0xE in Java.Lang.Thread.RunnableImplementor.Run at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Lang/Thread.cs:36,6	C#
0x8 in Java.Lang.IRunnableInvoker.n_Run at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net7.0/android-33/mcw/Java.Lang.IRunnable.cs:84,4	C#
0x8 in Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V at /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:22,5	C#

As I said, I think I have the Routing and the Dependencies set up correctly because simple navigation without passing an object works just fine.

I have also tried passing a string to the page like this:

await Shell.Current.GoToAsync($"{nameof(DetailPage)}?Text={training.Name}");

Then I adjusted the QueryProperty:
[QueryProperty("Text","Text")]
I also added to the viewmodel:
[ObservableProperty] text;
When I tried passing a string like this I had no problem and everything worked as expected.

But as soon as I try using the overload
public Task GoToAsync(ShellNavigationState state, IDictionary<string, object> parameters);
the application crashes and Visual Studio 2022 shows me the "Your application is in break mode" screen and a exception popup with "System.InvalidCastException: 'Object must implement IConvertible.'".

All of this is happening when I run it on the Android simulator.
When I try running it on Windows, it breaks in the "App.g.i.cs" (Autogenerated file)
in this if clause:

#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
    UnhandledException += (sender, e) =>
    {
        if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
    };
#endif

Also tried the hack of @ToolmakerSteve from this post:
https://stackoverflow.com/questions/75821516/maui-shell-navigation-fails-rare-reproduction
but this just makes the windows variant also immediately show me the "System.InvalidCastException" popup instead of breaking in the auto generated file
Thanks in Advance!

答案1

得分: 2

你正在混淆基于字符串的查询参数和基于对象的导航参数。

基于字符串的查询参数

您可以将 training 对象作为查询参数传递,前提是它是一个 string

await Shell.Current.GoToAsync(
    $"{nameof(DetailPage)}?DetailTraining={training}&Duration={10}",
    true);

然后在您的 ViewModelPage 中,您可以使用 [QueryProperty] 属性来接收这些属性:

[QueryProperty(nameof(DetailedTraining), "DetailTraining")]
[QueryProperty(nameof(Duration), nameof(Duration))]
public partial class DetailViewModel : ObservableObject
{
    [ObservableProperty]
    string detailedTraining;

    [ObservableProperty]
    int duration;
}

这使用了基于字符串的查询参数方法。

注意: 这仅适用于简单的值类型,如 stringintbool

基于对象的导航参数

如果您想要使用基于对象的导航参数方法来传递您自己的类和类型的对象,您需要在接收类上实现 IQueryAttributable 接口,例如您的 ViewModel:

public partial class DetailViewModel : ObservableObject, IQueryAttributable
{
    [ObservableProperty]
    Training detailedTraining;

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        DetailedTraining = query["DetailTraining"] as Training;
    }
}
英文:

You're mixing up string-based query parameters and object-based navigation parameters.

String-based query parameters

You can pass the training object as a query parameter, provided that it is a string:

await Shell.Current.GoToAsync(
    $&quot;{nameof(DetailPage)}?DetailTraining={training}&amp;Duration={10}&quot;,
    true);

In your ViewModel or Page, you can then use the [QueryProperty] attribute to receive the properties:

[QueryProperty(nameof(DetailedTraining),&quot;DetailTraining&quot;)]
[QueryProperty(nameof(Duration), nameof(Duration)]
public partial class DetailViewModel : ObservableObject
{
    [ObservableProperty]
    string detailedTraining;

    [ObservableProperty]
    int duration;
}

This uses the string-based query parameters approach.

Note: This only works with simple value types like string, int and bool.

Object-based navigation parameters

If you want to use the object-based navigation parameters approach to pass objects of your own classes and types, you'll need to implement the IQueryAttributable interface on your receiving class, e.g. your ViewModel:

public partial class DetailViewModel : ObservableObject, IQueryAttributable
{
    [ObservableProperty]
    Training detailedTraining;

    public void ApplyQueryAttributes(IDictionary&lt;string, object&gt; query)
    {
        DetailedTraining = query[&quot;DetailTraining&quot;] as Training;
    }
}

huangapple
  • 本文由 发表于 2023年6月6日 04:45:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76409887.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定