Search code examples
mvvmmauimaui-community-toolkit

General BindableProperty, System.InvalidCastException: 'Object must implement IConvertible.'


I am trying to use custom control with generic type in BindableProperty like so:

public partial class Overview : ContentView
{
    public static readonly BindableProperty AnimalsProperty =
            BindableProperty.Create(nameof(AnimalsProperty), typeof(ObservableRangeCollection<IAnimal>), typeof(Overview), null);

    public ObservableRangeCollection<IAnimal> Animals
    {
        get { return (ObservableRangeCollection<IAnimal>)GetValue(AnimalsProperty); }
        set { SetValue(AnimalsProperty, value); }
    }
}
public interface IAnimal {}
public partial class Dog: ObservableObject, IAnimal {}

DogOverview xaml:

<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DogOverview"
            >
    <Grid>
        <local:Overview
            Animals="{Binding Dogs}"
        />
    </Grid>
</ContentView>
public partial class DogViewModel: ObservableObject 
{
    [ObservableProperty]
    private ObservableRangeCollection<Dog> _dogs = [];
}

And when I use BindableContext in DogOverview like:

public partial class DogOverview : ContentView
{
    public DogOverview(DogViewModel vm)
    {
        InitializeComponent();
        BindingContext = vm;
    }
}

There is an error in line BindingContext = vm: **System.InvalidCastException:** 'Object must implement IConvertible.'. I've checked and this is happend because I use IAnimal in BindableProprety. I just want to create custom control with generic type and use it in another ContentPage, is it even possible?


Solution

  • Your BindableProperty can probably be reduce to either IEnumerable<object> or IEnumerable<IAnimal:

        public static readonly BindableProperty AnimalsProperty =
            BindableProperty.Create(nameof(AnimalsProperty), typeof(IEnumerable<IAnimal>), typeof(Overview), null);
    
        public IEnumerable<IAnimal> Animals
        {
            get { return (IEnumerable<IAnimal>)GetValue(AnimalsProperty); }
            set { SetValue(AnimalsProperty, value); }
        }
    

    I would probably add INotifyPropertyChanged to your IAnimal interface:

    public interface IAnimal : INotifyPropertyChanged
    {
    }
    

    As to the implementation of Dogs, there's no need for CommunityToolkit.Mvvm. This is because ObservableCollections are assigned once and it's their CollectionChanged events is what we're after, i.e.

    public partial class DogViewModel: ObservableObject 
    {
         public ObservableCollection<Dog> Dogs { get; } = new ();
    }