sthewissen/Xamarin.Forms.PancakeView

`Xamarin.Forms.BorderElement.OnBorderColorPropertyChanged` throws `System.InvalidCastException`

brminnick opened this issue · 8 comments

Problem

When the BorderColor changes (via a Binding or a DynamicResource), Xamarin.Forms.BorderElement.OnBorderColorPropertyChanged throws an InvalidCastException.

OnBorderColorPropertyChanged attempts to cast the PancakeView to an IBorderElement and a InvalidCastException is thrown because PancakeView does not implement IBorderElement.

Here's the link to the Xamarin.Forms source code that is throwing the InvalidCastException: https://github.com/xamarin/Xamarin.Forms/blob/719fc7a604ff0cce8922d717c99bfb0fa17e35e0/Xamarin.Forms.Core/BorderElement.cs#L19

Suggestion

Having PancakeView implement IBorderElement resolve the InvalidCastException.

public class PancakeView : ContentView, IBorderElement

Reproduction

Reproduction Steps

  • Download/Clone the PancakeView-Border-Repro branch of GitTrends: https://github.com/brminnick/GitTrends/tree/PancakeView-Border-Repro
  • In Visual Studio, open GitTrends.sln
  • In Visual Studio, set the startup project to GitTrends.iOS
  • In Visual Studio, set the build configuration to DEBUG
  • Build/deploy GitTrends.iOS to an iOS simulator / device
  • On the iOS device, wait for GitTrends to load
  • On the GitTrends Welcome Page page, on the bottom-right, click SKIP
  • On the GitTrends Connect to GitHub page, on the bottom-right, click TRY DEMO
  • Confirm the app crashes

Video

ScreenFlow

Stack Trace

Here is the stack trace from the by the reproduction sample, linked above.

BorderElement.OnBorderColorPropertyChanged (Xamarin.Forms.BindableObject bindable, System.Object oldValue, System.Object newValue)
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)
BindableObject.SetValueCore (Xamarin.Forms.BindableProperty property, System.Object value, Xamarin.Forms.Internals.SetValueFlags attributes, Xamarin.Forms.BindableObject+SetValuePrivateFlags privateAttributes)
Element.OnSetDynamicResource (Xamarin.Forms.BindableProperty property, System.String key)
BindableObject.SetDynamicResource (Xamarin.Forms.BindableProperty property, System.String key, System.Boolean fromStyle)
Element.SetDynamicResource (Xamarin.Forms.BindableProperty property, System.String key)
ElementExtensions.DynamicResources[TElement] (TElement element, System.ValueTuple`2[Xamarin.Forms.BindableProperty,System.String][] resources)
GitTrends.BaseRepositoryDataTemplate+CardView+CardViewFrame+ContentGrid+AvatarImage..ctor (System.String& avatarUrl) <0x104da828c + 0x0019b> in <b8c6e33f85b24874aaee293c45607f59#5b4fab3a7ddfb8e11daefb7ac1e330ab>:0
GitTrends.BaseRepositoryDataTemplate+CardView+CardViewFrame+ContentGrid..ctor (System.Collections.Generic.IEnumerable`1[Xamarin.Forms.View]& parentDataTemplateChildren, GitTrends.Shared.Repository& repository) <0x104da75f0 + 0x007d3> in <b8c6e33f85b24874aaee293c45607f59#5b4fab3a7ddfb8e11daefb7ac1e330ab>:0
GitTrends.BaseRepositoryDataTemplate+CardView+CardViewFrame..ctor (System.Collections.Generic.IEnumerable`1[Xamarin.Forms.View]& parentDataTemplateChildren, GitTrends.Shared.Repository& repository) <0x104da74f4 + 0x000b3> in <b8c6e33f85b24874aaee293c45607f59#5b4fab3a7ddfb8e11daefb7ac1e330ab>:0
GitTrends.BaseRepositoryDataTemplate+CardView..ctor (System.Collections.Generic.IEnumerable`1[Xamarin.Forms.View]& parentDataTemplateChildren, GitTrends.Shared.Repository& repository) <0x104da7108 + 0x00353> in <b8c6e33f85b24874aaee293c45607f59#5b4fab3a7ddfb8e11daefb7ac1e330ab>:0
<.ctor>b__0 ()
ElementTemplate.CreateContent ()
TemplatedCell.Bind (Xamarin.Forms.DataTemplate template, System.Object bindingContext, Xamarin.Forms.ItemsView itemsView)
ItemsViewController`1[TItemsView].UpdateTemplatedCell (Xamarin.Forms.Platform.iOS.TemplatedCell cell, Foundation.NSIndexPath indexPath)
ItemsViewController`1[TItemsView].GetCell (UIKit.UICollectionView collectionView, Foundation.NSIndexPath indexPath)
ItemsViewController`1[TItemsView].GetPrototype ()
ItemsViewLayout.DetermineCellSize ()
ListViewLayout.ConstrainTo (CoreGraphics.CGSize size)
ItemsViewController`1[TItemsView].CheckForEmptySource ()
ItemsViewController`1[TItemsView].NumberOfSections (UIKit.UICollectionView collectionView)
(wrapper managed-to-native) ObjCRuntime.Messaging.objc_msgSendSuper(intptr,intptr)
UICollectionViewLayout.PrepareLayout ()
ItemsViewLayout.PrepareLayout ()
(wrapper managed-to-native) UIKit.UIApplication.UIApplicationMain(int,string[],intptr,intptr)
UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate)
UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName)
Application.Main (System.String[] args)

Thanks for the clear repro, I'll look into it.

I'm wondering though how/why XamForms suddenly decides that this is a BorderElement. I'm not referencing that anywhere, so it has to have been changed somewhere along the inheritance chain from ContentView upwards. Either way, I implemented the interface, but pretty much cleared out any implementation for it, as all of that is actually done through the Border subclass. That way it won't get in the way of the current implementation. So I did this:

public Color BorderColor => default(Color);
int IBorderElement.CornerRadius => 0;
public double BorderWidth => 0;
public int CornerRadiusDefaultValue => 0;
public Color BorderColorDefaultValue => default(Color);
public double BorderWidthDefaultValue => 0;
public bool IsCornerRadiusSet() => false;
public bool IsBackgroundColorSet() => false;
public bool IsBorderColorSet() => false;
public bool IsBorderWidthSet() => false;

However, that creates new issues:

In GitTrends > Views > Repository > BaseRepositoryDataTemplate.cs:L68

Content = new ContentGrid(parentDataTemplateChildren, repository);

image

Could not set up parent class, due to: Parent class vtable failed to initialize, due to: VTable setup of type Xamarin.Forms.PancakeView.PancakeView failed at GitTrends.BaseRepositoryDataTemplate+CardView+CardViewFrame..ctor (System.Collections.Generic.IEnumerable`1[Xamarin.Forms.View]& parentDataTemplateChildren, GitTrends.Shared.Repository& repository) [0x00054] in /Users/sthewissen/Downloads/GitTrends-PancakeView-Border-Repro/GitTrends/Views/Repository/BaseRepositoryDataTemplate.cs:68 
  at GitTrends.BaseRepositoryDataTemplate+CardView..ctor (System.Collections.Generic.IEnumerable`1[Xamarin.Forms.View]& parentDataTemplateChildren, GitTrends.Shared.Repository& repository) [0x000af] in /Users/sthewissen/Downloads/GitTrends-PancakeView-Border-Repro/GitTrends/Views/Repository/BaseRepositoryDataTemplate.cs:51 
  at GitTrends.BaseRepositoryDataTemplate+<>c__DisplayClass7_0.<.ctor>b__0 () [0x00000] in /Users/sthewissen/Downloads/GitTrends-PancakeView-Border-Repro/GitTrends/Views/Repository/BaseRepositoryDataTemplate.cs:28 
  at Xamarin.Forms.ElementTemplate.CreateContent () [0x00026] in D:\a\1\s\Xamarin.Forms.Core\ElementTemplate.cs:82 
  at Xamarin.Forms.Platform.iOS.TemplatedCell.Bind (Xamarin.Forms.DataTemplate template, System.Object bindingContext, Xamarin.Forms.ItemsView itemsView) [0x00052] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\TemplatedCell.cs:91 
  at Xamarin.Forms.Platform.iOS.ItemsViewController`1[TItemsView].UpdateTemplatedCell (Xamarin.Forms.Platform.iOS.TemplatedCell cell, Foundation.NSIndexPath indexPath) [0x00012] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ItemsViewController.cs:199 
  at Xamarin.Forms.Platform.iOS.ItemsViewController`1[TItemsView].GetCell (UIKit.UICollectionView collectionView, Foundation.NSIndexPath indexPath) [0x00033] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ItemsViewController.cs:84 
  at Xamarin.Forms.Platform.iOS.ItemsViewController`1[TItemsView].GetPrototype () [0x0005d] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ItemsViewController.cs:263 
  at Xamarin.Forms.Platform.iOS.ItemsViewLayout.DetermineCellSize () [0x0004e] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ItemsViewLayout.cs:201 
  at Xamarin.Forms.Platform.iOS.ListViewLayout.ConstrainTo (CoreGraphics.CGSize size) [0x0001e] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ListViewLayout.cs:16 
  at Xamarin.Forms.Platform.iOS.ItemsViewController`1[TItemsView].CheckForEmptySource () [0x00039] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ItemsViewController.cs:114 
  at Xamarin.Forms.Platform.iOS.ItemsViewController`1[TItemsView].NumberOfSections (UIKit.UICollectionView collectionView) [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ItemsViewController.cs:181 
  at (wrapper managed-to-native) ObjCRuntime.Messaging.void_objc_msgSendSuper(intptr,intptr)
  at UIKit.UICollectionViewLayout.PrepareLayout () [0x00023] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.20.2.2/src/Xamarin.iOS/UIKit/UICollectionViewLayout.g.cs:576 
  at Xamarin.Forms.Platform.iOS.ItemsViewLayout.PrepareLayout () [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.iOS\CollectionView\ItemsViewLayout.cs:385 
  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.20.2.2/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.20.2.2/src/Xamarin.iOS/UIKit/UIApplication.cs:65 
  at GitTrends.iOS.Application.Main (System.String[] args) [0x00000] in /Users/sthewissen/Downloads/GitTrends-PancakeView-Border-Repro/GitTrends.iOS/Main.cs:5 

Fun! Investigation continues, but happy to hear thoughts 😅

@brminnick I downgraded the PancakeView version in that GitTrends branch to v2.1.0.714 and it seems to work (it doesn't throw an exception at least). Apparently along the way while opening the can of worms that is AndroidX something changed that is impacting this in v2.2.* as that was the biggest change in there. I've subsequently pulled all the v2.2.* stuff from NuGet as I'm getting other issues related to this as well (e.g. #134).

Regarding an actual solution, I'm not quite sure when I'll get around to it. Other things in life are taking up a lot of time and will be for the foreseeable future.

2.2 is again there. Does it mean this issue was resolved?

Not quite sure where you got that impression from, as there's currently no listed version 2.2.*?

image

ahh ok. I just recognized that my private feed cached 2.2 versions when i had package source=all on Nuget manager, it was pulling from my private feed :)

@brminnick I assume this regressed now in v2.3.754-beta? As the addition of IBorderElement is basically causing #141. I'm clueless with regards to the vtable nonsense its spouting and where that's coming from, to be honest. I'm also pretty limited in time already and expect that to not change any time soon (👶 incoming).

Congrats on the baby!!

Yup - I confirmed System.InvalidCastException is happening in v2.3.754-beta

Thanks! So either we solve this strange IBorderElement cast (which is happening for some reason that I don't quite grasp yet) or we solve the vtable issue when implementing IBorderElement (which is possibly even vaguer). Why is it casting to IBorderElement? I would assume that a check needs to happen there if whatever it's casting even implements it. If it's not implemented somewhere up the inheritance chain for PancakeView, should XamForms even be assuming it is?