英文:
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);
然后在您的 ViewModel 或 Page 中,您可以使用 [QueryProperty]
属性来接收这些属性:
[QueryProperty(nameof(DetailedTraining), "DetailTraining")]
[QueryProperty(nameof(Duration), nameof(Duration))]
public partial class DetailViewModel : ObservableObject
{
[ObservableProperty]
string detailedTraining;
[ObservableProperty]
int duration;
}
这使用了基于字符串的查询参数方法。
注意: 这仅适用于简单的值类型,如 string
、int
和 bool
。
基于对象的导航参数
如果您想要使用基于对象的导航参数方法来传递您自己的类和类型的对象,您需要在接收类上实现 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(
$"{nameof(DetailPage)}?DetailTraining={training}&Duration={10}",
true);
In your ViewModel or Page, you can then use the [QueryProperty]
attribute to receive the properties:
[QueryProperty(nameof(DetailedTraining),"DetailTraining")]
[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<string, object> query)
{
DetailedTraining = query["DetailTraining"] as Training;
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论