5

I have just migrated my Xamarin Forms project to .NET Standard 2.0 and am fighting with some odd behavior. In the following situation, I am getting a Null Reference Exception on a ListDictionaryInternal with no exception breakpoint, no stack trace and no other information available. The situation is, from my view model I am setting the bindable text property on a custom button control. I have debugged all the way through the text setting process and the exception happens after. I am completely stumped by this one. And to be clear, this code was working before the .NET Standard 2.0 migration. Thank you in advance!

UPDATE:

So this exception has started happening with Forms iOS as well but now I am able to get a stack trace when the System.Exception breakpoint hits. Also this is not related to .NET Standard 2.0 because this is also happening when targeting a PCL 4.5 project, Profile 111 to be exact.

I can set a breakpoint in the code behind's OnSizeAllocated override method and see that the view has accepted the bound text and laid out properly. So it is not the binding that is the issue but rather something to do with the laying out of the view.

Also, for clarification, this exception only happens if the text is set with a bindable property. If the text is set in the xaml the exception does not happen.

enter image description here

The ViewModel...

public class SimpleSearchViewModel : BaseViewModel
{
    enum SearchCatagories
    {
        All,
        SubjectId,
        PublicationNumber
    }

    public override void OnStart()
    {
        UpdateTypeButton("ALL");
    }

    private void UpdateTypeButton(string item)
    {
        SearchTypeButtonText = item;
    }

    private string _searchTypeButtonText;
    public string SearchTypeButtonText
    {
        get
        {
            return _searchTypeButtonText;
        }
        private set
        {
            _searchTypeButtonText = value;
            OnPropertyChanged(nameof(SearchTypeButtonText));
        }
    }
}

The View...

<core:BasePage xmlns="http://xamarin.com/schemas/2014/forms" 
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
xmlns:viewmodels="using:App.ViewModels"
xmlns:customcontrols="clr-namespace:App;assembly=App"
xmlns:core="clr-namespace:App.Core;assembly=App.Core"
x:Class="Pages.SimpleSearchPage"
Title="Simple Search">

<ContentPage.BindingContext>
    <viewmodels:SimpleSearchViewModel />
</ContentPage.BindingContext>

<ContentPage.Content>

<StackLayout Padding=" 10, 10, 10, 10" 
             HorizontalOptions="FillAndExpand" 
             VerticalOptions="FillAndExpand" 
             Orientation="Horizontal" > 

            <customcontrols:SVGImageButton x:Name="TypeSelectionButton" 
                            HorizontalOptions="Start" 
                            VerticalOptions="Center"
                            ButtonPressedCommand="{Binding TypeButtonClickedCommand}"
                               SVGImageName="SVGImages.ic_triangle_down.svg"
                            CommandParameter="{x:Reference TypeSelectionButton}"
                            ButtonText="{Binding SearchTypeButtonText}"
                            ButtonBackgroundColor="{Binding ButtonBackgroundColor}"/>

        </StackLayout>
    </ContentPage.Content>
</core:BasePage>

The SVGImageButton...

[XamlCompilation(XamlCompilationOptions.Compile)]
public class SVGImageButton : ContentView
{
    private readonly Button _button;
    private readonly SvgCachedImage _svgImage;

    public static BindableProperty ButtonTextProperty =    BindableProperty.Create(nameof(ButtonText), typeof(string),   typeof(SVGImageButton), string.Empty, BindingMode.OneWay, propertyChanged: (bindable, oldValue, newValue) =>
    {
        if (newValue == null) return;
        var control = (SVGImageButton)bindable;
        control.ButtonText = newValue.ToString();
    });

    public string ButtonText
    {
        get
        {
            return _button.Text;
        }
        set
        {
            _button.Text = value; 
        }
    }

    public string SVGImageName { get; set; }

    protected override void OnParentSet()
    {
        base.OnParentSet();

        _button.Text = ButtonText;
        _svgImage.Source = SvgImageSource.FromResource(SVGImageName);
     }

    public SVGImageButton()
    {
        var content = new RelativeLayout();

        _button = new Button { BackgroundColor = Color.Gray, TextColor = Color.Black };
        _svgImage = new SvgCachedImage { IsEnabled = false };

        content.Children.Add(_button,
                             Constraint.RelativeToParent((parent) =>
                             {
                                 return parent.X;
                             }),
                             Constraint.RelativeToParent((parent) =>
                             {
                                 return parent.Y;
                             }),
                             Constraint.RelativeToParent((parent) =>
                             {
                                 return parent.Width;
                             }),
                             Constraint.RelativeToParent((parent) =>
                             {
                                 return parent.Height;
                             }));

        content.Children.Add(_svgImage,
                           Constraint.RelativeToParent((parent) =>
                           {
                               return parent.Width - (parent.Height / 2) - (parent.Height / 4);
                           }),
                           Constraint.RelativeToParent((parent) =>
                           {
                               return parent.Height - (parent.Height /     2) - (parent.Height / 4);
                           }),
                           Constraint.RelativeToParent((parent) =>
                           {
                               return parent.Height / 2;
                           }),
                           Constraint.RelativeToParent((parent) =>
                           {
                               return parent.Height / 2;
                           }));

        Content = content;
        }
    }
}
Sev
  • 883
  • 1
  • 14
  • 34

1 Answers1

0

So this issue ended up coming from how I was handling the sizing of the control in the OnMeasure callback. The correct question and answer can be seen here.

Sev
  • 883
  • 1
  • 14
  • 34