ObservableCollection causing invalid cast exception

I'm completely new to XAML and have been trying to build a small phone app based on Microsoft's sample here: ListView binding. When my code runs I get a "Specified cast is not valid" exception at the point marked below. I'm not sure how much of this is relevant but bear with me.

This is the XAML code for part of the page:

<ListView x:Name="ListCoords" ItemsSource="{Binding Coordinates}">
<ListView.ItemTemplate>
    <DataTemplate>
        <StackLayout Margin="0" Orientation="Horizontal">
            <Label x:Name="LabelA" HeightRequest="32" WidthRequest="100" Text="{Binding Path=angle, StringFormat='{0:F2}'}" />
            <Label x:Name="LabelX" HeightRequest="32" WidthRequest="100" Text="{Binding Path=x, StringFormat='{0:F2}'}" />
            <Label x:Name="LabelY" HeightRequest="32" WidthRequest="100" Text="{Binding Path=y, StringFormat='{0:F2}'}" />
        </StackLayout>
    </DataTemplate>
</ListView.ItemTemplate>

The relevant parts of the cs code behind the page is like this:

public partial class PCDPage : ContentPage
{

    public ObservableCollection<Coordinate> CoordinateList = new ObservableCollection<Coordinate> ();
    public ObservableCollection<Coordinate> Coordinates { get { return CoordinateList; } }

    public PCDPage()
    {
        InitializeComponent();
        ListCoords.ItemsSource = CoordinateList;
    }

    ... Code here responds to a button press and calls the following ...
    
    //
    // Calculate and display a table of hole coordinates
    //
    private void CalculateCoordinates()
    {
        // Start a new list
        CoordinateList.Clear();

        double x, y, a, b;

        for (int i = 0; i < count; i++)
        {
            ... Calculate a, x, y
            
            // Add the corrdinate to the list
            CoordinateList.Add(new Coordinate(a, x, y));      <<<< - Exception
        }

        ... Finish off
    }

And the little coordinate class is:

    public class Coordinate
{
    public Coordinate()
    {

    }

    public Coordinate(double va, double vx, double vy)
    {
        angle = va;
        x = vx;
        y = vy;
    }

    public double angle { get; set; }
    public double x { get; set; }
    public double y { get; set; }
}

When I run my app it crashes at the point marked on adding the first coordinate to the list. While it's stopped in the debugger, I can hover over the CoordinateList and see that it contains one item as expected. In the XAML, I can do the same thing with the binding. So it looks to me as if the problem occurs after adding the item and before returning to my code. Have I missed something obvious or is there a subtlety that I haven't learnt about yet?

Thanks in advance


Solution 1:

The properties of the Coordinate class are of type double, but the "Text" property of the Labels is string. You need to create a converter

public  class DoubleToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        return value!=null?((double)value).ToString():"";
    }
 
    public object ConvertBack(object value, Type targetType, 
        object parameter, CultureInfo culture)
    {
        return null;
    }
}

<ListView x:Name="ListCoords" ItemsSource="{Binding Coordinates}">
    <ListView.Resources>
        <l:DoubleToStringConverter x:Key="converter" />
    </ListView.Resources>
<ListView.ItemTemplate>
    <DataTemplate>
        <StackLayout Margin="0" Orientation="Horizontal">
            <Label x:Name="LabelA" HeightRequest="32" WidthRequest="100" Text="{Binding Path=angle, StringFormat='{0:F2}',Converter={StaticResource converter}}" />
            <Label x:Name="LabelX" HeightRequest="32" WidthRequest="100" Text="{Binding Path=x, StringFormat='{0:F2}',Converter={StaticResource converter}}" />
            <Label x:Name="LabelY" HeightRequest="32" WidthRequest="100" Text="{Binding Path=y, StringFormat='{0:F2}',Converter={StaticResource converter}}" />
        </StackLayout>
    </DataTemplate>
</ListView.ItemTemplate>