Reputation: 51
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: 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!
Upvotes: 2
Views: 1716
Reputation: 8883
You're mixing up string-based query parameters and object-based navigation 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
.
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;
}
}
Upvotes: 4