Xamarin.Forms 5.0.0.2012, Visual Studio 2019 for Mac 8.10.16
I am encountering a problem (on both iOS and Android, on both emulators and physical devices) trying to call a Converter in the XAML describing a new screen in our app. This is the markup:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:internal="clr-namespace:Views.Shared"
x:Class="Views.EditPage">
<ContentPage.Resources>
<internal:PictureNameToImageSourceConverter x:Key="PictureNameToImageSourceConverter" />
</ContentPage.Resources>
...
<CollectionView
x:Name="picturesView"
ItemsLayout="HorizontalList"
HeightRequest="90">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="False" Padding="5">
<Image
HeightRequest="80"
Source="{Binding Path=., Converter={StaticResource PictureNameToImageSourceConverter}}" />
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
The CollectionView.ItemsSource value is set in code to a List<string> instance. When the List has no items, the screen displays correctly. When it does have items the app crashes as the screen appears. If I launch the app attached to the VS debugger, the screen freezes before the crash, and I never get any information.
This converter is well tested and is used several other places in the app with no problem. When I replace the Image with <Label Text="{Binding Path=.}"/> the text items are displayed as expected, so it doesn't look like a binding or Path syntax error.
Is there something I'm not seeing or not aware of in the markup that's causing this? Or can anyone suggest further debugging I haven't thought of?
UPDATE
A breakpoint on the very first line of the converter was never reached.
EDIT in response to comments:
From the EditPage codebehind:
public partial class EditPage : ContentPage
{
internal List<string> PictureNames;
...
protected override void OnAppearing( )
{
base.OnAppearing();
picturesView.ItemsSource = PictureNames;
}
The PictureNames property is actually set by a different Page to which one navigates from the EditPage:
private void saveSelection_Click(object sender, System.EventArgs args)
{
creator.PictureNames = new List<string>();
foreach (SelectableItem<Picture> item in pictureItems)
{
if (item.IsSelected)
{
creator.PictureNames.Add(item.Item.PictureName);
}
}
Navigation.PopAsync();
}
pictureItems is a List acting as a ListView.ItemsSource on that screen, where pictures are selected or unselected.
UPDATE
After much setting of breakpoints, I've determined that the line picturesView.ItemsSource = PictureNames; is where the crash happens. It seems odd that it only happens when the template is showing an Image, but not a Label, seeing that the converter is never actually called.
UPDATE
The trick of adding the delay did get me to the breakpoint. And what I found is more puzzling than ever: The value parameter being passed to the Convert method of our converter is null. This is the case whether coming back from the picture selection screen, or if I set the bound list in response to a Button rather than in OnAppearing, or if I just set it right in the page constructor.
In addition, when setting a breakpoint on the crashing line, when the display element in the template is a Label everything is as expected, but when the display element is an Image the debugger freezes when trying to look at those values. The problem is apparently something about the fact of calling a converter in this precise situation.
I tested that by adding a different converter to the template:
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="False" Padding="5" HeightRequest="{Binding Path=., Converter={StaticResource PictureNameToHeightConverter}}">
<Label Text="{Binding Path=.}" />
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
Exactly the same result: The value parameter passed to the Convert method is null even though the same bound value in the same template instance is displayed in the Label. If I set a breakpoint on the line assigning the ItemsSource property as before, the debugger freezes.
UPDATE
Finally beginning to suspect that I'm triggering some corner case bug in the framework, I replaced the CollectionView with a CarouselView, and it works correctly:
<CarouselView
x:Name="picturesView"
HeightRequest="90">
<CarouselView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="False" Padding="5">
<Image
HeightRequest="80"
Source="{Binding Path=., Converter={StaticResource PictureNameToImageSourceConverter}}" />
</Frame>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
I'm adding this as an update to the question rather than as an answer because I still don't actually know the explanation.
I've seen some odd behavior when updating page details in
OnAppearing.Hard to put a finger on exactly when there will be a problem, but the fact that you navigated to another page, so this is
OnAppearingduring the "back" from that other page is probably a factor.Try this:
At minimum, this should allow Xamarin to reach the breakpoint in the converter.
——————-
UPDATE
I don’t see any obvious flaw in the code you’ve posted.
You’ve narrowed it down to the line it crashes on.
Yet a breakpoint at start of converter is never reached.
As you say, this is a puzzling combination of facts.
Here is a test to do:
That is, remove all the complexity of going to another page, querying values, returning to this page.
I bet this will work. Then you’d have the best situation for debugging: a case that works vs. one that doesn’t.
After that, its “divide and conquer”. Start making the working one more like the broken one, and/or vice versa, until the culprit is identified.