levitali / CompiledBindings

MIT License
267 stars 14 forks source link

[MAUI] Specified cast is not valid #38

Open Alex-Dobrynin opened 3 months ago

Alex-Dobrynin commented 3 months ago

If I try to add x:Bind to an object which is not Element but only BindableObject I'm getting the exception specified in the title. If I use regular Binding, then it works as expected.

For example I have

public class Pin : BindableObject
{
    // Assume this is BindableProperty
    public string Address { get; set; }
}

And if I do in XAML smth like this

<Pin Address="{x:Bind SomeProperty}" />

I'm getting 'Specified cast is not valid'

levitali commented 3 months ago

Can you provide a sample project?

Because Pin class is not a view, you can set it XAML only to some property.

I have a custom View ContentControl with DataContent property of type object. When I set it like this:

<local:ContentControl>
    <local:ContentControl.DataContent>
        <local:Pin Address="{x:Bind SomeProperty}" />
    </local:ContentControl.DataContent>
</local:ContentControl>

it works.

Alex-Dobrynin commented 3 months ago

I'm just using Maui.GoogleMaps. and it does have Map control, which has ItemsSource. Also it has ItemTemplate, which expects DataTemplate be typeof(Pin), where Pin is BindaleObject. and when I'm trying to do x:Bind inside this DataTemplate I'm getting the error I mentioned in the title

<googlemaps:Map ItemsSource="{x:Bind Pins}">
    <googlemaps:Map.ItemTemplate>
        <DataTemplate x:DataType="googlemaps:Pin">
            <googlemaps:Pin Position="{x:Bind Position}" />
        </DataTemplate>
    </googlemaps:Map.ItemTemplate>
</googlemaps:Map>

As you can see, the DataType for DataTemplate also typeof(Pin)

levitali commented 3 months ago

Data Templates should only be used to specify appearance of a data item. Look at documentation.

A DataTemplate is used to specify the appearance of data

Because of this, generated code expects, that content of a DataTemplate is derivied from Element:

static void BindingsChanged(global::Microsoft.Maui.Controls.BindableObject bindable, object oldValue, object newValue)
{
    if (oldValue != null)
    {
        ((IGeneratedDataTemplate)oldValue).Cleanup((global::Microsoft.Maui.Controls.Element)bindable);
    }
    if (newValue != null)
    {
        ((IGeneratedDataTemplate)newValue).Initialize((global::Microsoft.Maui.Controls.Element)bindable);
    }
}

Also Visual Studio displays a warning because of misused DataTemplate:

Untitled

I've looked at the code of the Map control, which is also developed by you. I don't quite understand the need of ItemsSource and ItemTemplate properties. The pins can be created and added directly to Pins property. Or it can be settable.

Alex-Dobrynin commented 3 months ago

The code wasn't developed by me. I just did PR with another changes to it. But the original implementation is from Xamarin.Forms.GoogleMaps. The package Maui.GoogleMaps is migrated version for XF one by @themronion. Why it was made so I don't know. but it is working even with that warning you showed in the picture.

But. maybe you can change the implementation of generated code to expect BindableObject instead of Element?

levitali commented 3 months ago

It involves many changes. The code, generated for a DataTemplate, needs that the root object is an Element.

Maybe I will do it in the future.