Bind to Index in Collection Xamarin

Viewed 1636

enter image description here

public List<Scoreboard> RawScoreboard { get; set; }

public ObservableCollection<Scoreboard> ScoreboardList { get; set; }


  <CollectionView ItemsSource="{Binding ScoreboardList}"
                        EmptyView="Sorry, no scores yet! Play a game and view your results here!">
            <CollectionView.ItemTemplate>
                <DataTemplate  x:DataType="models:Scoreboard">
                    <Grid BackgroundColor="{StaticResource BasePurple}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>

                        <Label Text="{Binding Score}" Style="{StaticResource LabelStyle}" />

                        <Label Grid.Column="1" Text="{Binding UserName}" Style="{StaticResource LabelStyle}" />

                        <Label Grid.Column="2" Text="{Binding CountryName}" Style="{StaticResource LabelStyle}" />

                        <Label Grid.Column="3" Style="{StaticResource LabelStyle}" >
                            <Label.FormattedText>
                                <FormattedString>
                                    <Span Text="{Binding Score}"/>
                                    <Span Text="/10"/>
                                </FormattedString>
                            </Label.FormattedText>
                        </Label>

                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>

So what I want to do is to change the Rank column to be bound it it's own index in the list. I.e 1, 2, 3, 4, 5, 6. Just a numbered list here. I think this might be possible to do in linq maybe?

public class Scoreboard
{
    public int UserId { get; set; }

    public int CountryId { get; set; }

    public string CountryName { get; set; }

    public string UserName { get; set; }

    public int Score { get; set; }
}

public async Task OnAppearing()
        {
            await GetScores();
            SortScoreboard();

            foreach (var scoreboard in RawScoreboard)
            {
                ScoreboardList.Add(scoreboard);
            }
    }

I've added all relevant code, so basically. "How do I bind a element to it's own index inside a collectionview?"

1 Answers

It is somewhat of a hack. But you can create a ValueConverter, which takes the CollectionView as a parameter and tries to get the index from that:

public class IndexValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (parameter is Binding binding && 
            value is Scoreboard score && 
            binding.Source is CollectionView collectionView && 
            collectionView.BindingContext is ScoreboardViewModel viewModel)
        {
            return viewModel.ScoreboardList.IndexOf(score);
        }

        return -1;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
}

Remember to register your converter:

<ContentPage.Resources>
    <ResourceDictionary>
        <converters:IndexValueConverter x:Key="IndexConverter" />
    </ResourceDictionary>
</ContentPage.Resources>

This way you can bind your Label to the item itself and use the reference in the ConverterParameter to look up the index from:

<Label Text="{Binding ., Converter={StaticResource IndexConverter}, ConverterParameter={Binding Source={x:Reference Scores}, Path=BindingContext}}" />

Given that you called your CollectionView "Scores" with x:Name="Scores".

This yields something like:

screenshot of test app

Related