Assign an object to a BindableProperty in ResourceDictionary

Viewed 47

I try to initialize and assign an instance of a custom class to a BindableProperty in a ResourceDictionary (styles.xaml).

My class:

namespace Foo {
  public class Colors {
    public Color Positive { get; set; }
    public Color Negative { get; set; }
    public Color Neutral { get; set; }

    public Colors() { }
  }
}

My class with BindableProperty:

namespace Foo {
  public class Bar : ContentView {
    public Colors Colors {
      get => (Colors)GetValue(ColorsProperty);
      set => SetValue(ColorsProperty, value);
    }

   public static BindableProperty ColorsProperty = 
            BindableProperty.Create(nameof(Colors), typeof(Colors), 
            typeof(Bar), null, BindingMode.OneWay);

styles.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    xmlns:foo="clr-namespace:Foo;assembly=Foo">
  <foo:Colors
        x:Key="colors"
        Positive="Green"
        Negative="Red"
        Neutral="Blue"/>

  ...

  <Style TargetType="foo:Bar">
    <Setter Property="Colors" Value="{StaticResource colors}"/>
  </Style>
</ResourceDictionary>

A breakpoint in the constructor of class Foo.Colors is hit, so an instance of this class is created during loading styles.xaml. However loading styles.xaml crashes with an unhandled NullReferenceException inside Mono.Android. When i delete the assignment of the Color-Property everything works fine, so the assignment must be the problem.

Any suggestions?

Exception Details:

System.NullReferenceException: Object reference not set to an instance of an object.
  at Android.Runtime.JNINativeWrapper._unhandled_exception (System.Exception e) [0x0000e] in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:12 
  at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPL_V (_JniMarshal_PPL_V callback, System.IntPtr jnienv, System.IntPtr klazz, System.IntPtr p0) [0x0001d] in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:111 
  at (wrapper native-to-managed) Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPL_V(intptr,intptr,intptr)

The Exception occures in App.xaml.g.cs, after global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(App));

2 Answers

I found a simple solution that works for me:

In styles.xaml I replaced StaticResource with DynamicResource:

<Style TargetType="foo:Bar">
  <Setter Property="Colors" Value="{DynamicResource colors}"/>
</Style>

Thanks for all suggestions!

It looks like you're wanting to use a stand-alone resource dictionary. I've had trouble with these in the past when I've followed Microsofts documentation on them. I've always had to set the dictionary as if it's in another assembly:

styles.xaml:

<ResourceDictionary
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:foo="clr-namespace:DELETEME"
    x:Class="DELETEME.styles">

    <foo:Colors
        x:Key="colors"
        Positive="Green"
        Negative="Red"
        Neutral="Blue"/>

    <Style TargetType="foo:Bar">
        <Setter Property="Colors" Value="{StaticResource colors}"/>
    </Style>
</ResourceDictionary>

Note that this file does require a simple code behind:

namespace DELETEME
{
    public partial class styles : ResourceDictionary
    {
        public styles()
        {
            InitializeComponent();
        }
    }
}

The easiest way to add this file is add a ContentView with xaml, then change the xaml and cs to inherit from ResourceDictionary.

From there, add the file to the merged dictionaries of the app or page:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:foo="clr-namespace:DELETEME"
         x:Class="DELETEME.App">

    <Application.Resources>
       <ResourceDictionary>
           <ResourceDictionary.MergedDictionaries>
               <foo:styles />
           </ResourceDictionary.MergedDictionaries>
       </ResourceDictionary>
    </Application.Resources>
</Application>

This should resolve your errors. Example below.

Bar.cs class:

public class Bar : ContentView
{
    public static readonly BindableProperty ColorsProperty =
        BindableProperty.Create(nameof(Colors), typeof(Colors), typeof(Bar));

    public Colors Colors
    {
        get => (Colors)GetValue(ColorsProperty);
        set => SetValue(ColorsProperty, value);
    }

    public Bar()
    {
        var b1 = new BoxView { BackgroundColor = Colors.Negative };
        var b2 = new BoxView { BackgroundColor = Colors.Neutral };
        var b3 = new BoxView { BackgroundColor = Colors.Positive };

        var sl = new StackLayout();

        sl.Children.Add(b1);
        sl.Children.Add(b2);
        sl.Children.Add(b3);

        Content = sl;
    }
}

MainPage.xaml class:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:foo="clr-namespace:DELETEME"
         x:Class="DELETEME.MainPage">

    <StackLayout>
        <foo:Bar />
    </StackLayout>

</ContentPage>

Running Result:

enter image description here

Related