Search code examples
c#xamarinxamarin.formsxamarin.androidxamarin.ios

Xamarin Forms CollectionView doesn't show properly


I have a problem. I created a CollectionView with a custom ViewPage to place a grid with rowspan. The ItemSource is set using a ViewModel. Now when I scroll in the CollectionView the rows are randomly placed on the screen, even inside each other.

Here is my xaml code:

<CollectionView ItemsSource="{Binding agentOrderList}" Margin="5,0,5,0" HeightRequest="450">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Vertical" ItemSpacing="0"/>
    </CollectionView.ItemsLayout>

    <CollectionView.ItemTemplate>
        <DataTemplate>
            <local:OrderView BindingContext="{Binding }"/>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Here is the ViewModel:

public class VM_AgentsPage : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private ObservableCollection<OrderBundle> _agentOrderList;
    public ObservableCollection<OrderBundle> agentOrderList
    {
        get
        {
            return _agentOrderList;
        }
        set
        {
            if (_agentOrderList != value)
            {
                _agentOrderList = value;
                OnPropertyChanged();
            }
        }
    }


    public VM_AgentsPage()
    {
        agentOrderList = new ObservableCollection<OrderBundle>();
        LoadAgentOrders_Handler();
    }


    public Task LoadAgentOrders_Handler()
    {
        return LoadAgentOrders();
    }
    private async Task LoadAgentOrders()
    {
        IsRefreshingAgentOrders = true;

        App.AgentOrderList = await App.RestService.GetAgentOrders(App.AgentId);

        ObservableCollection<OrderBundle> tempAgentOrderList = new ObservableCollection<OrderBundle>();

        foreach (OrderBundle orderBundle in App.AgentOrderList)
        {
            tempAgentOrderList.Add(orderBundle);
        }

        agentOrderList = tempAgentOrderList;

        IsRefreshingAgentOrders = false;
    }
}

And finally the OrderView.xaml.cs:

public partial class OrderView : ContentView
{
    Grid grid;

    public OrderView()
    {
        InitializeComponent();


        grid = new Grid
        {
            ColumnDefinitions =
        {
            new ColumnDefinition{ Width = new GridLength(1, GridUnitType.Star)},
            new ColumnDefinition{ Width = new GridLength(1, GridUnitType.Star) },
            new ColumnDefinition{ Width = new GridLength(1, GridUnitType.Star) },
            new ColumnDefinition{ Width = new GridLength(1, GridUnitType.Star) },
            new ColumnDefinition{ Width = new GridLength(1, GridUnitType.Star) }
        }
        };

        grid.RowSpacing = 0;

        this.Content = grid;


        this.BindingContextChanged += View1_BindingContextChanged;
    }

    private void View1_BindingContextChanged(object sender, EventArgs e)
    {
        var obj = this.BindingContext as OrderBundles;

        if (obj != null)
        {
            List<Trade> list = obj.Trades;

            for(int i = 0; i < list.Count; i++)
            {

                grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(20) });

                Trade trade = list[i];
                grid.Children.Add(new Label
                {
                    Text = trade.Date
                }, 0, i);
                grid.Children.Add(new Label
                {
                    Text = trade.Action
                }, 1, i);
                grid.Children.Add(new Label
                {
                    Text = trade.Coin
                }, 2, i);
                grid.Children.Add(new Label
                {
                    Text = trade.Price.ToString()
                }, 3, i);              
            }

            Label label = new Label
            {
                Text = list[0].ProfitUSDT,
                TextColor = Color.Green ,
                VerticalTextAlignment = TextAlignment.Center
            };

            grid.Children.Add(label, 4, 0);


            Grid.SetRowSpan(label, list.Count);
        }
    }
}

This is what it looks like in the beginning: enter image description here

But when I scroll it starts looking like this: enter image description here

What is causing this?


Solution

  • I found BindingContextChanged method triggers when scroll the collectionview , so it keeps adding RowDefinition and controls , we should clear the list at the beginning .

        private void View1_BindingContextChanged(object sender, EventArgs e)
        {
            //add the following two lines
            grid.RowDefinitions.Clear();
            grid.Children.Clear();
    

    enter image description here