xamarin / Xamarin.Forms

Xamarin.Forms is no longer supported. Migrate your apps to .NET MAUI.
https://aka.ms/xamarin-upgrade
Other
5.63k stars 1.87k forks source link

[Bug] Carousel view inside carousel view conflict on position #15800

Open Vannoctar2 opened 1 year ago

Vannoctar2 commented 1 year ago

Description

Hello,

I am contacting you because I have a bug in my program that I was able to reproduce. I have a carouselview nested inside another. Scrolling inside wasn't working so I disabled the parent one and did it manually. The problem does not arise there.

When scrolling down to the parent carousel, everything is fine. When scrolling at the level of the child carousel, everything is fine.

The problem arises when trying to add a new item in the parent's collection.

Here is my code. Very easy to reproduce 👍

TestPage.xaml:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:model="clr-namespace:MathomicsEnseignant.Views.SubViews"
             x:Class="MathomicsEnseignant.Views.SubViews.TestPage"
             x:Name="_TestPage">
    <ContentPage.Content>
      <StackLayout>
            <CarouselView
                x:Name="_carousel"
                ItemsSource="{Binding Items}"                
                IsSwipeEnabled="False">
                <CarouselView.ItemTemplate>
                    <DataTemplate>
                        <StackLayout>
                            <Label Text="{Binding .}" TextColor="Black" FontSize="Header" HorizontalTextAlignment="Center"/>
                            <model:Inside/>
                        </StackLayout>
                    </DataTemplate>
                </CarouselView.ItemTemplate>

            </CarouselView>
            <StackLayout Orientation="Horizontal">
                <Button Text="Left"  Clicked="SwipeLeft"/>
                <Button Text="AddItem" Command="{Binding CommandAddItem}"/>
                <Button Text="Right" Clicked="SwipeRight"/>
            </StackLayout>
        </StackLayout>
  </ContentPage.Content>
</ContentPage>

TestPage.xaml.cs

using MathomicsEnseignant.Test.ViewModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace MathomicsEnseignant.Views.SubViews
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class TestPage : ContentPage
    {
        public TestPage()
        {
            InitializeComponent();
            this.BindingContext = new TestPageViewModel();

        }

        public void SwipeLeft(object sender, EventArgs e)
        {
            ObservableCollection<string> collection = (ObservableCollection<string>)this._carousel.ItemsSource;
            int currentIndex = collection.IndexOf((string)_carousel.CurrentItem);
            int nextIndex = currentIndex - 1;
            if (nextIndex < 0) nextIndex = collection.Count - 1;
            _carousel.ScrollTo(nextIndex);
            _carousel.Position = nextIndex;
            _carousel.CurrentItem = collection[nextIndex];
        }

        public void SwipeRight(object sender, EventArgs e)
        {
            ObservableCollection<string> collection = (ObservableCollection<string>)this._carousel.ItemsSource;

            int currentIndex = collection.IndexOf((string)_carousel.CurrentItem);
            int nextIndex = currentIndex + 1;
            if (nextIndex >= collection.Count) nextIndex = 0;

            _carousel.ScrollTo(nextIndex);
            _carousel.Position = nextIndex;
            _carousel.CurrentItem = collection[nextIndex];
        }

    }
}

TestPageViewModel:

using MathomicsEnseignant.ViewModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using Xamarin.Forms;

namespace MathomicsEnseignant.Test.ViewModels
{
    public class TestPageViewModel: BaseViewModel
    {
        public ObservableCollection<String> Items { get; set; } = new ObservableCollection<string>();

        public Command CommandAddItem { get; set; } 

        public TestPageViewModel()
        {
            CommandAddItem = new Command(AddItem);
            for (int i = 0; i < 5; i++) this.Items.Add(this.Items.Count + "");
        }

        public void AddItem()
        {
            this.Items.Add(this.Items.Count + "");
        }

    }
}

Inside.xaml:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MathomicsEnseignant.Views.SubViews.Inside"
             x:Name="_inside">
    <ContentView.Content>
      <StackLayout>
            <CarouselView
                ItemsSource="{Binding Items2}" >
                <CarouselView.ItemTemplate>
                    <DataTemplate>
                        <Label Text="{Binding .}" TextColor="Black" FontSize="Header" HorizontalTextAlignment="Center"/>
                    </DataTemplate>
                </CarouselView.ItemTemplate>
            </CarouselView>
        </StackLayout>
  </ContentView.Content>
</ContentView>

Inside.xaml.cs:

using MathomicsEnseignant.Test.ViewModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace MathomicsEnseignant.Views.SubViews
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Inside : ContentView
    {
        public Inside()
        {
            InitializeComponent();
            this.BindingContext = new InsidePageViewModel();

        }
    }
}

InsidePageViewModel:

using MathomicsEnseignant.ViewModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using Xamarin.Forms;

namespace MathomicsEnseignant.Test.ViewModels
{
    public class InsidePageViewModel: BaseViewModel
    {
        public ObservableCollection<String> Items2 { get; set; } = new ObservableCollection<string>();

        public InsidePageViewModel()
        {
            for (int i = 0; i < 5; i++) this.Items2.Add("Inside " + this.Items2.Count + "");
        }

    }
}

Steps to Reproduce

1- Scroll parent items with "left" and "right" buttons 2- Scroll through the child carousel. 3- Click on AddItem (middle button)

Expected Behavior

The last Item must be added at the end of the carousel view. We keep the same view and the carouselview inside does not change.

Actual Behavior

We stay on the same page, but the carouselview seems to have moved to another parent item.

Basic Information

I run the application on an android emulator pixel_5 api_30 and i use Visual studio with Xamarin Forms

This is my last post in french :

Thank you for your help.

Bug Xamarin.webm

Vannoctar2 commented 1 year ago

Another problem: if I navigate from the end to the beginning several times. Child elements seem to flow from one parent to another.

Vannoctar2 commented 1 year ago

Okay, a custom carouselview revolved the problem.