Xamarin.forms: [Bug] Hot Reload crashes when resource in App.xaml changes

Created on 24 Jan 2020  路  9Comments  路  Source: xamarin/Xamarin.Forms

Description

I experience a crash when Shell is used and I change a resource defined in App.xaml. I have 3 pages (FlyoutItems) defined in Shell, each page uses label style defined in App.xaml. If I navigate to 1st or 2nd page and change the style, crash occurs. Crash doesn't occur when I'm navigated to 3rd (last) page and I change the style.

Steps to Reproduce

  1. Open attached solution and run the app
  2. Navigate to Settings page (2nd page)
  3. Change color defined in TextStyle in App.xaml and save the file to trigger Hot Reload
  4. Crash occurs

Expected Behavior

No crash occurring after updating the style.

Actual Behavior

App crashes with exception:

System.ArgumentException: 'Ambiguous routes matched for: //IMPL_about/IMPL_about/about/ matches found: //IMPL_about/IMPL_about/about,//IMPL_about/IMPL_about/about
Parameter name: uri'

Basic Information

  • Version with issue: 4.4.0.991477

Screenshots

settings_page
app xaml
hot_reload_crash

Reproduction Link

HotReloadResourceCrash.zip

VS bug #1079452

shell external-hotreload high unverified bug

Most helpful comment

@viktorszekeress

TL;DR The actual bug is with Xamarin.Forms Shell not being able to handle reapplying Shell routes. It's invoked via Hot Reload, but the underlying issue is with Forms, so there's nothing for me to fix.

I _am_ working on more incremental ways of editing the XAML Resources that may sidestep bugs like this one, but the actual underlying bug (Reapplying routes on reloads) needs to be addressed.


The issue here, as far as I can tell, is specific to using Xamarin.Forms shell and having assets that end up touching AppShell, causing it to reapply routes. The other issues you noted (Crashing when editing App.xaml, Merged Dictionaries) are _all related_ to this underlying Shell issue: If you're seeing Ambiguous routes matched for being thrown on a change, that is this bug, no matter what it's coming from, since it's calling the same code to reapply the routes.

If you try to replicate it in a non-shell app, they (should) work fine. It's because this is a Shell app that's causing this issue. If you can reproduce that in a _non-shell_ app, then that's something else for me to check out.

The Forms code, as far as I can see, only assumes that the routes will be set once, and never again. That, I believe, needs to be changed so that it can be altered and updated if a user does a Hot Reload.

All 9 comments

I can confirm I'm seeing this as well, however my exception is different

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'GroupableItemsViewController`1'.
  at Foundation.NSObject.get_SuperHandle () [0x00012] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.10.0.17/src/Xamarin.iOS/Foundation/NSObject2.cs:449
  at UIKit.UICollectionViewController.get_CollectionView () [0x0002a] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.10.0.17/src/Xamarin.iOS/UICollectionViewController.g.cs:1101
  at Xamarin.Forms.Platform.iOS.ItemsViewController`1[TItemsView].RemeasureLayout (Xamarin.Forms.VisualElement formsElement) [0x00076] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ItemsViewController.cs:316
  at Xamarin.Forms.Platform.iOS.ItemsViewController`1[TItemsView].HandleFormsElementMeasureInvalidated (Xamarin.Forms.VisualElement formsElement) [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ItemsViewController.cs:331
  at Xamarin.Forms.Platform.iOS.StructuredItemsViewController`1[TItemsView].HandleFormsElementMeasureInvalidated (Xamarin.Forms.VisualElement formsElement) [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\StructuredItemsViewController.cs:186
  at Xamarin.Forms.Platform.iOS.ItemsViewController`1[TItemsView].OnFormsElementMeasureInvalidated (System.Object sender, System.EventArgs e) [0x0000a] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ItemsViewController.cs:325
  at at (wrapper delegate-invoke) <Module>.invoke_void_object_EventArgs(object,System.EventArgs)
  at Xamarin.Forms.VisualElement.InvalidateMeasureInternal (Xamarin.Forms.Internals.InvalidationTrigger trigger) [0x0000b] in D:\a\1\s\Xamarin.Forms.Core\VisualElement.cs:816
  at Xamarin.Forms.Grid.InvalidateMeasureInternal (Xamarin.Forms.Internals.InvalidationTrigger trigger) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Grid.cs:230
  at Xamarin.Forms.Layout.OnChildMeasureInvalidated (Xamarin.Forms.VisualElement child, Xamarin.Forms.Internals.InvalidationTrigger trigger) [0x00090] in D:\a\1\s\Xamarin.Forms.Core\Layout.cs:355
  at Xamarin.Forms.Layout.OnChildMeasureInvalidated (System.Object sender, System.EventArgs e) [0x00013] in D:\a\1\s\Xamarin.Forms.Core\Layout.cs:212
  at Xamarin.Forms.VisualElement.InvalidateMeasureInternal (Xamarin.Forms.Internals.InvalidationTrigger trigger) [0x0000b] in D:\a\1\s\Xamarin.Forms.Core\VisualElement.cs:816
  at Xamarin.Forms.VisualElement.OnIsVisibleChanged (System.Boolean oldValue, System.Boolean newValue) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\VisualElement.cs:847
  at Xamarin.Forms.Layout.OnIsVisibleChanged (System.Boolean oldValue, System.Boolean newValue) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Layout.cs:399
  at Xamarin.Forms.VisualElement+<>c.<.cctor>b__265_4 (Xamarin.Forms.BindableObject bindable, System.Object oldvalue, System.Object newvalue) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\VisualElement.cs:149
  at Xamarin.Forms.BindableObject.SetValueActual (Xamarin.Forms.BindableProperty property, Xamarin.Forms.BindableObject+BindablePropertyContext context, System.Object value, System.Boolean currentlyApplying, Xamarin.Forms.Internals.SetValueFlags attributes, System.Boolean silent) [0x00120] in D:\a\1\s\Xamarin.Forms.Core\BindableObject.cs:463
  at Xamarin.Forms.BindableObject.SetValueCore (Xamarin.Forms.BindableProperty property, System.Object value, Xamarin.Forms.Internals.SetValueFlags attributes, Xamarin.Forms.BindableObject+SetValuePrivateFlags privateAttributes) [0x00173] in D:\a\1\s\Xamarin.Forms.Core\BindableObject.cs:397
  at Xamarin.Forms.BindingExpression.ApplyCore (System.Object sourceObject, Xamarin.Forms.BindableObject target, Xamarin.Forms.BindableProperty property, System.Boolean fromTarget) [0x00220] in D:\a\1\s\Xamarin.Forms.Core\BindingExpression.cs:156
  at Xamarin.Forms.BindingExpression.Apply (System.Object sourceObject, Xamarin.Forms.BindableObject target, Xamarin.Forms.BindableProperty property) [0x0006b] in D:\a\1\s\Xamarin.Forms.Core\BindingExpression.cs:71
  at Xamarin.Forms.Binding.Apply (System.Object context, Xamarin.Forms.BindableObject bindObj, Xamarin.Forms.BindableProperty targetProperty, System.Boolean fromBindingContextChanged) [0x0006d] in D:\a\1\s\Xamarin.Forms.Core\Binding.cs:136
  at Xamarin.Forms.BindableObject.ApplyBindings (System.Boolean skipBindingContext, System.Boolean fromBindingContextChanged) [0x00041] in D:\a\1\s\Xamarin.Forms.Core\BindableObject.cs:480
  at Xamarin.Forms.BindableObject.SetInheritedBindingContext (Xamarin.Forms.BindableObject bindable, System.Object value) [0x0005a] in D:\a\1\s\Xamarin.Forms.Core\BindableObject.cs:209
  at Xamarin.Forms.Element.SetChildInheritedBindingContext (Xamarin.Forms.Element child, System.Object context) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:505
  at Xamarin.Forms.Element.<OnBindingContextChanged>b__82_0 (Xamarin.Forms.BindableObject child, System.Object bc) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:308
  at Xamarin.Forms.BindableObjectExtensions.PropagateBindingContext[T] (Xamarin.Forms.BindableObject self, System.Collections.Generic.IList`1[T] children, System.Action`2[T1,T2] setChildBindingContext) [0x0002c] in D:\a\1\s\Xamarin.Forms.Core\BindableObjectExtensions.cs:28
  at Xamarin.Forms.Element.OnBindingContextChanged () [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:306
  at Xamarin.Forms.View.OnBindingContextChanged () [0x0000c] in D:\a\1\s\Xamarin.Forms.Core\View.cs:158
  at Xamarin.Forms.Grid.OnBindingContextChanged () [0x00006] in D:\a\1\s\Xamarin.Forms.Core\Grid.cs:147
  at Xamarin.Forms.BindableObject.SetInheritedBindingContext (Xamarin.Forms.BindableObject bindable, System.Object value) [0x00062] in D:\a\1\s\Xamarin.Forms.Core\BindableObject.cs:210
  at Xamarin.Forms.Element.SetChildInheritedBindingContext (Xamarin.Forms.Element child, System.Object context) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:505
  at Xamarin.Forms.Element.<OnBindingContextChanged>b__82_0 (Xamarin.Forms.BindableObject child, System.Object bc) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:308
  at Xamarin.Forms.BindableObjectExtensions.PropagateBindingContext[T] (Xamarin.Forms.BindableObject self, System.Collections.Generic.IList`1[T] children, System.Action`2[T1,T2] setChildBindingContext) [0x0002c] in D:\a\1\s\Xamarin.Forms.Core\BindableObjectExtensions.cs:28
  at Xamarin.Forms.Element.OnBindingContextChanged () [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:306
  at Xamarin.Forms.View.OnBindingContextChanged () [0x0000c] in D:\a\1\s\Xamarin.Forms.Core\View.cs:158
  at Xamarin.Forms.BindableObject.SetInheritedBindingContext (Xamarin.Forms.BindableObject bindable, System.Object value) [0x00062] in D:\a\1\s\Xamarin.Forms.Core\BindableObject.cs:210
  at Xamarin.Forms.Element.SetChildInheritedBindingContext (Xamarin.Forms.Element child, System.Object context) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:505
  at Xamarin.Forms.Element.<OnBindingContextChanged>b__82_0 (Xamarin.Forms.BindableObject child, System.Object bc) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:308
  at Xamarin.Forms.BindableObjectExtensions.PropagateBindingContext[T] (Xamarin.Forms.BindableObject self, System.Collections.Generic.IList`1[T] children, System.Action`2[T1,T2] setChildBindingContext) [0x0002c] in D:\a\1\s\Xamarin.Forms.Core\BindableObjectExtensions.cs:28
  at Xamarin.Forms.Element.OnBindingContextChanged () [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:306
  at Xamarin.Forms.View.OnBindingContextChanged () [0x0000c] in D:\a\1\s\Xamarin.Forms.Core\View.cs:158
  at Xamarin.Forms.Grid.OnBindingContextChanged () [0x00006] in D:\a\1\s\Xamarin.Forms.Core\Grid.cs:147
  at Xamarin.Forms.BindableObject.SetInheritedBindingContext (Xamarin.Forms.BindableObject bindable, System.Object value) [0x00062] in D:\a\1\s\Xamarin.Forms.Core\BindableObject.cs:210
  at Xamarin.Forms.Element.set_Parent (Xamarin.Forms.Element value) [0x000dc] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:201
  at Xamarin.Forms.Element.OnChildRemoved (Xamarin.Forms.Element child) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:335
  at Xamarin.Forms.VisualElement.OnChildRemoved (Xamarin.Forms.Element child) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\VisualElement.cs:750
  at Xamarin.Forms.Layout.OnInternalRemoved (Xamarin.Forms.View view) [0x00012] in D:\a\1\s\Xamarin.Forms.Core\Layout.cs:473
  at Xamarin.Forms.Layout.InternalChildrenOnCollectionChanged (System.Object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x0002b] in D:\a\1\s\Xamarin.Forms.Core\Layout.cs:436
  at System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x00018] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/ObservableCollection.cs:263
  at System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedAction action, System.Object item, System.Int32 index) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/ObservableCollection.cs:338
  at System.Collections.ObjectModel.ObservableCollection`1[T].RemoveItem (System.Int32 index) [0x00021] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/external/corefx/src/System.ObjectModel/src/System/Collections/ObjectModel/ObservableCollection.cs:182
  at System.Collections.ObjectModel.Collection`1[T].Remove (T item) [0x00027] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/external/corefx/src/Common/src/CoreLib/System/Collections/ObjectModel/Collection.cs:128
  at Xamarin.Forms.ContentPresenter.OnContentChanged (Xamarin.Forms.BindableObject bindable, System.Object oldValue, System.Object newValue) [0x00034] in D:\a\1\s\Xamarin.Forms.Core\ContentPresenter.cs:82
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__7_0 (System.Object state) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1021
  at Foundation.NSAsyncSynchronizationContextDispatcher.Apply () [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.10.0.17/src/Xamarin.iOS/Foundation/NSAction.cs:178
  at at (wrapper managed-to-native) UIKit.UIApplication.UIApplicationMain(int,string[],intptr,intptr)
  at UIKit.UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate) [0x00005] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.10.0.17/src/Xamarin.iOS/UIKit/UIApplication.cs:86
  at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0000e] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.10.0.17/src/Xamarin.iOS/UIKit/UIApplication.cs:65
  at MyApp.Mobile.Field.iOS.Application.Main (System.String[] args) [0x00001] in /Users/chaseflorell/Projects/MyApp/MyApp.Mobile.Field/MyApp.Mobile.Field.iOS/Main.cs:12

This issue also occurs when any of my pages includes resource dictionary from file (via MergedDictionaries). If Settings page includes SomeResources.xaml and I change a resource in SomeResources.xaml, I get the same crash.
Thus, the only workaround is having all resources declared in all pages.

Can somebody take a look at this issue?

@viktorszekeress good call on the Merged Dictionaries, I've got that too when I see the crash.

<?xml version="1.0" encoding="UTF-8"?>
<Application xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             ...>
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <xaml:Colors />
                <xaml:GlobalStyles />
                <xaml:TemplateStyles />
                <xaml:Fonts />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

This is a problem with hot reload. Routes are registered twice when the shell XAML file is edited. After restarting the app everything is normal.

Xamarin Forms 4.5

Hey folks, Hot Reload dev here 馃憢

@GuidoNeele @viktorszekeress

The issue you're seeing here is, as far as I can tell, is directly to do with Shell itself being invoked via Hot Reload. The resource dictionaries, however they're set up, don't matter since the Shell routes are being reinvoked, causing the Ambiguous routes matched for error to be thrown. You can invoke the same error editing any of the lines inside of the AppShell.xaml too, and you can reproduce this in the Xaminals shell sample.

Generally, in the current implementation of XAML Hot Reload, when you make a change to the App.xaml, we only invoke XamlLoader.LoadResources, which replaces the loaded resources in your app. For other changes (RDs, ContentPages, etc), it uses XamlLoader.Load

In the case of a Shell app, if you touch elements that end up poking at the AppShell, it ends up reloading the AppShell, which then causes the routes to be reapplied, causing the error you see.

Now, we (Hot Reload) are working on more enhance editing of changes that could avoid the issue you see here (since you're not changing anything directly in Shell, it shouldn't be trying to reinvoke the routes). However, the underlying issue in Forms still exists. If you actually _were_ trying to edit the routes on your page, the code handling routes would be reapplied, causing the bug to appear again.

@ChaseFlorell The issue you're seeing is unrelated to this one: From what I can tell, there seems to be a bug with how Xamarin.Forms handles the virtualization of the CollectionView, hence it poking elements that are disposed of. Again, this feels more like an issue invoked via Hot Reload, but not something that can solely be fixed _within_ Hot Reload. I would file that separately if it's not already.

Hi @drasticactions, thanks for comprehensive explanation!

From my understanding:
You are now working on fix that will solve the issue when changing resources in App.xaml crashes the app, by avoiding re-invoking the routes in Shell.xaml.

What about the case when I change resources in Shell.xaml? I don't have such scenario with my app, but I can imagine other devs might have it.

Another question - how about my case about merged dictionaries (referenced in pages as MergedDictionaries), fits in the bug description? Does changing resources in dictionary referenced as MergedDictionary invoke routes as well?

@viktorszekeress

TL;DR The actual bug is with Xamarin.Forms Shell not being able to handle reapplying Shell routes. It's invoked via Hot Reload, but the underlying issue is with Forms, so there's nothing for me to fix.

I _am_ working on more incremental ways of editing the XAML Resources that may sidestep bugs like this one, but the actual underlying bug (Reapplying routes on reloads) needs to be addressed.


The issue here, as far as I can tell, is specific to using Xamarin.Forms shell and having assets that end up touching AppShell, causing it to reapply routes. The other issues you noted (Crashing when editing App.xaml, Merged Dictionaries) are _all related_ to this underlying Shell issue: If you're seeing Ambiguous routes matched for being thrown on a change, that is this bug, no matter what it's coming from, since it's calling the same code to reapply the routes.

If you try to replicate it in a non-shell app, they (should) work fine. It's because this is a Shell app that's causing this issue. If you can reproduce that in a _non-shell_ app, then that's something else for me to check out.

The Forms code, as far as I can see, only assumes that the routes will be set once, and never again. That, I believe, needs to be changed so that it can be altered and updated if a user does a Hot Reload.

That clears thing up. Thanks!

This is fixed on the latest version of Forms

I tested the original reproduction and it works fine when updated to XF 4.6

Was this page helpful?
0 / 5 - 0 ratings