Disconnecting Map Handler causes Map to crash on second page entrance and moving to region.
Description
In my company application we have a map control with logic that moves Map to specific region when data loads. We have a custom Handler for which we call DisconnectHandler.
After second time page is opened and region is changed there is a crash with
System.InvalidOperationException: VirtualView cannot be null here
I've prepared a Sandbox application that reproduces the issue easily: https://github.com/awasilik/MauiMapBug
Steps to Reproduce
- Open Sandbox application
- Click OpenMap button
- Let the region change and press "Go back" button
- Click OpenMap button again
Crash occurs
Link to public reproduction project repository
https://github.com/awasilik/MauiMapBug
Version with bug
8.0.6 SR1
Is this a regression from previous behavior?
No, this is something new
Last version that worked well
Unknown/Other
Affected platforms
iOS
Affected platform versions
iPhone 8 with iOS 15.6 (other iPhones have the same issue)
Did you find any workaround?
Commenting out base.DisconnectHandler(mapView);
in custom map handler.
Relevant log output
System.InvalidOperationException: VirtualView cannot be null here
at Microsoft.Maui.Handlers.ViewHandler`2[[Microsoft.Maui.Maps.IMap, Microsoft.Maui.Maps, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Maps.Platform.MauiMKMapView, Microsoft.Maui.Maps, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].get_VirtualView()
at Microsoft.Maui.Maps.Handlers.MapHandler.Microsoft.Maui.Maps.Handlers.IMapHandler.get_VirtualView()
at Microsoft.Maui.Maps.Platform.MauiMKMapView.MkMapViewOnRegionChanged(Object sender, MKMapViewChangeEventArgs e)
at MapKit.MKMapView._MKMapViewDelegate.RegionChanged(MKMapView mapView, Boolean animated) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/build/dotnet/ios/generated-sources/MapKit/MKMapView.g.cs:line 1956
at MapKit.MKMapView.SetRegion(MKCoordinateRegion region, Boolean animated) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/build/dotnet/ios/generated-sources/MapKit/MKMapView.g.cs:line 759
at Microsoft.Maui.Maps.Handlers.MapHandler.MoveToRegion(MapSpan mapSpan, Boolean animated)
at Microsoft.Maui.Maps.Handlers.MapHandler.MapMoveToRegion(IMapHandler handler, IMap map, Object arg)
at Microsoft.Maui.CommandMapper`2.<>c__DisplayClass6_0[[Microsoft.Maui.Maps.IMap, Microsoft.Maui.Maps, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Maps.Handlers.IMapHandler, Microsoft.Maui.Maps, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].<Add>b__0(IElementHandler h, IElement v, Object o)
at Microsoft.Maui.CommandMapper.InvokeCore(String key, IElementHandler viewHandler, IElement virtualView, Object args)
at Microsoft.Maui.CommandMapper.Invoke(IElementHandler viewHandler, IElement virtualView, String property, Object args)
at Microsoft.Maui.Handlers.ElementHandler.Invoke(String command, Object args)
at Microsoft.Maui.Controls.Maps.Map.MoveToRegion(MapSpan mapSpan)
at MapBugApp.MapPage.<MoveMap>b__1_0() in /Users/ADWA/Desktop/MapBugApp/MauiMapBug/MapBugApp/MapPage.xaml.cs:line 24
at Foundation.NSAsyncActionDispatcher.Apply() in /Users/builder/azdo/_work/1/s/xamarin-macios/src/Foundation/NSAction.cs:line 150
at UIKit.UIApplication.UIApplicationMain(Int32 argc, String[] argv, IntPtr principalClassName, IntPtr delegateClassName) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/UIKit/UIApplication.cs:line 58
at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/UIKit/UIApplication.cs:line 94
at MapBugApp.Program.Main(String[] args) in /Users/ADWA/Desktop/MapBugApp/MauiMapBug/MapBugApp/Platforms/iOS/Program.cs:line 13
2024-02-15 15:40:52.268 MapBugApp[99574:28420853] Unhandled managed exception: VirtualView cannot be null here (System.InvalidOperationException)
at Microsoft.Maui.Handlers.ViewHandler`2[[Microsoft.Maui.Maps.IMap, Microsoft.Maui.Maps, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Maps.Platform.MauiMKMapView, Microsoft.Maui.Maps, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].get_VirtualView()
at Microsoft.Maui.Maps.Handlers.MapHandler.Microsoft.Maui.Maps.Handlers.IMapHandler.get_VirtualView()
at Microsoft.Maui.Maps.Platform.MauiMKMapView.MkMapViewOnRegionChanged(Object sender, MKMapViewChangeEventArgs e)
at MapKit.MKMapView._MKMapViewDelegate.RegionChanged(MKMapView mapView, Boolean animated) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/build/dotnet/ios/generated-sources/MapKit/MKMapView.g.cs:line 1956
at MapKit.MKMapView.SetRegion(MKCoordinateRegion region, Boolean animated) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/build/dotnet/ios/generated-sources/MapKit/MKMapView.g.cs:line 759
at Microsoft.Maui.Maps.Handlers.MapHandler.MoveToRegion(MapSpan mapSpan, Boolean animated)
at Microsoft.Maui.Maps.Handlers.MapHandler.MapMoveToRegion(IMapHandler handler, IMap map, Object arg)
at Microsoft.Maui.CommandMapper`2.<>c__DisplayClass6_0[[Microsoft.Maui.Maps.IMap, Microsoft.Maui.Maps, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Maps.Handlers.IMapHandler, Microsoft.Maui.Maps, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].<Add>b__0(IElementHandler h, IElement v, Object o)
at Microsoft.Maui.CommandMapper.InvokeCore(String key, IElementHandler viewHandler, IElement virtualView, Object args)
at Microsoft.Maui.CommandMapper.Invoke(IElementHandler viewHandler, IElement virtualView, String property, Object args)
at Microsoft.Maui.Handlers.ElementHandler.Invoke(String command, Object args)
at Microsoft.Maui.Controls.Maps.Map.MoveToRegion(MapSpan mapSpan)
at MapBugApp.MapPage.<MoveMap>b__1_0() in /Users/ADWA/Desktop/MapBugApp/MauiMapBug/MapBugApp/MapPage.xaml.cs:line 24
at Foundation.NSAsyncActionDispatcher.Apply() in /Users/builder/azdo/_work/1/s/xamarin-macios/src/Foundation/NSAction.cs:line 150
at UIKit.UIApplication.UIApplicationMain(Int32 argc, String[] argv, IntPtr principalClassName, IntPtr delegateClassName) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/UIKit/UIApplication.cs:line 58
at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/UIKit/UIApplication.cs:line 94
at MapBugApp.Program.Main(String[] args) in /Users/ADWA/Desktop/MapBugApp/MauiMapBug/MapBugApp/Platforms/iOS/Program.cs:line 13
=================================================================
Native Crash Reporting
=================================================================
Got a SIGABRT while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
=================================================================
Native stacktrace:
=================================================================
0x10487e5b0 - /private/var/containers/Bundle/Application/7000E81D-4B95-4035-9FA5-AC7499DC165B/MapBugApp.app/MapBugApp : AppleCryptoNative_X509ImportCollection
0x10486949c - /private/var/containers/Bundle/Application/7000E81D-4B95-4035-9FA5-AC7499DC165B/MapBugApp.app/MapBugApp : AppleCryptoNative_X509ImportCollection
0x1049b7a30 - /private/var/containers/Bundle/Application/7000E81D-4B95-4035-9FA5-AC7499DC165B/MapBugApp.app/MapBugApp : AppleCryptoNative_X509ImportCollection
0x10487dda0 - /private/var/containers/Bundle/Application/7000E81D-4B95-4035-9FA5-AC7499DC165B/MapBugApp.app/MapBugApp : AppleCryptoNative_X509ImportCollection
0x1dc05cd48 - /usr/lib/system/libsystem_platform.dylib : <redacted>
0x1dc074854 - /usr/lib/system/libsystem_pthread.dylib : pthread_kill
0x18b4056ac - /usr/lib/system/libsystem_c.dylib : abort
0x1045562c8 - /private/var/containers/Bundle/Application/7000E81D-4B95-4035-9FA5-AC7499DC165B/MapBugApp.app/MapBugApp : xamarin_find_protocol_wrapper_type
0x1047b4264 - /private/var/containers/Bundle/Application/7000E81D-4B95-4035-9FA5-AC7499DC165B/MapBugApp.app/MapBugApp : AppleCryptoNative_X509ImportCollection
0x104853fbc - /private/var/containers/Bundle/Application/7000E81D-4B95-4035-9FA5-AC7499DC165B/MapBugApp.app/MapBugApp : AppleCryptoNative_X509ImportCollection
0x104568fb8 - /private/var/containers/Bundle/Application/7000E81D-4B95-4035-9FA5-AC7499DC165B/MapBugApp.app/MapBugApp : xamarin_get_original_working_directory_path
0x1048bc4f8 - /private/var/containers/Bundle/Application/7000E81D-4B95-4035-9FA5-AC7499DC165B/MapBugApp.app/MapBugApp : AppleCryptoNative_X509ImportCollection
0x104f984d0 - Unknown
=================================================================
Basic Fault Address Reporting
=================================================================
Memory around native instruction pointer (0x1bb5e6bbc):0x1bb5e6bac c0 03 5f d6 c0 03 5f d6 10 29 80 d2 01 10 00 d4 .._..._..)......
0x1bb5e6bbc e3 00 00 54 fd 7b bf a9 fd 03 00 91 91 ee ff 97 ...T.{..........
0x1bb5e6bcc bf 03 00 91 fd 7b c1 a8 c0 03 5f d6 c0 03 5f d6 .....{...._..._.
0x1bb5e6bdc fd 7b bf a9 fd 03 00 91 50 00 80 d2 01 10 00 d4 .{......P.......
=================================================================
Managed Stacktrace:
=================================================================
=================================================================
We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.
I am also experiencing this issue. In my case I'm seeing the error when clearing and adding MapElements to my map on the second map page load. Thanks to @awasilik for the workaround of commenting out DisconnectHandler().
The problem is the handler constructor and the use of a MapPool.
protected override MauiMKMapView CreatePlatformView()
{
return MapPool.Get() ?? new MauiMKMapView(this);
}
```
it will reuse the MapView from the pool, but it was constructed with a previous handler that was disposed (virtual view will be null).
a work around I found is to overload the DisconnectHandler to prevent the call to MapPool.Add(platformView) in the base class.
I think a good way to fix this problem is to allow the handler to be set outside of the constructor, and we can just set it after we get the map from the pool.
This is still an issue with .NET 9. With .NET 9, the disconnect handler is called automatically upon view disposal, witch requires revisiting all view using the control to opt-out from automatic disconnect.
I agree this is still an issue - any work around as it makes map unusable - I have same use case as timdog- it blows up on adding MapElements
Does anyone have an example of overriding the disconnect handler?
Hi @michaelonz ,
Simply override the DisconnectHandler in you custom handler as shown below: In my case, I'm disconnecting a few event handlers, and commented out the call to the base.DiconnectHandler.
Have in mind that the DisconnectHandler should not be called normally in projects targeting <.Net 9 (i.e. in .NET 8). This is only happening in .NET 9, and you can opt out by using the following attached property on the control: HandlerProperties.DisconnectPolicy="Manual"
protected override void DisconnectHandler(MauiMKMapView platformView) { platformView.DidSelectAnnotationView -= CustomMapHandler_DidSelectAnnotationView; platformView.GetViewForAnnotation -= GetViewForAnnotation; platformView.RegionChanged -= PlatformView_RegionChanged; //base.DisconnectHandler(platformView); }
You can disconnect the handler in xaml for your custom handler for .net 9:
<maps:Map
x:Name="map"
HandlerProperties.DisconnectPolicy="Manual"
IsScrollEnabled="False"
ItemsSource="{Binding Pins}">
</maps:Map>
and in your CustomMapHandler (just for iOS) as I used this example, example:
protected override void DisconnectHandler(MauiMKMapView platformView)
{
platformView.GetViewForAnnotation -= GetViewForAnnotations;
}
FYI, I'm facing another issue with Maps on .NET 9, possibly related to this. The app crashes under some special circumstances with invalid cast exception. More details can be found here:
https://github.com/dotnet/maui/issues/26000
This issue still happens when manipulating MapElements using Microsoft.Maui.Controls.Maps 9.0.40.
This makes the control unusable. Do we have a fix/workaround for this?
EDIT - using HandlerProperties.DisconnectPolicy="Manual" does seem to suppress the exception.
You can disconnect the handler in xaml for your custom handler for .net 9:
<maps:Map x:Name="map" HandlerProperties.DisconnectPolicy="Manual" IsScrollEnabled="False" ItemsSource="{Binding Pins}"> </maps:Map>and in your CustomMapHandler (just for iOS) as I used this example, example:
protected override void DisconnectHandler(MauiMKMapView platformView) { platformView.GetViewForAnnotation -= GetViewForAnnotations; }
@patkozlowski The HandlerProperties.DisconnectPolicy="Manual" change has stopped the app from crashing so far. I it also necessary to override the disconnect handler? Not sure what that is accomplishing.
Thank you @patkozlowski for the solution, it works by setting the disconnectpolicy to manual. We create a map in the code and add it to the xaml, so for anyone using the same route, we set the policy as follows:
_map.SetValue(Microsoft.Maui.Controls.HandlerProperties.DisconnectPolicyProperty, Microsoft.Maui.HandlerDisconnectPolicy.Manual);