I defined a CarouselView with the use of left and right arrows.
The problem is that in my views, there are graphic elements which take quite a long time to load.
When I press the scroll arrows several times in a row and quickly, I get the following error:
MAUI Java.Lang.IllegalStateException: 'The specified child already has a parent. You must call removeView() on the child's parent first.'
I think it's two things. Or a bug related to SkiaSharp and SKCanvasView (these elements are in the carouselview views). Either because I scroll through the views even though the scrolling is not finished.
Here is an example of my code.
using MathomicsEnseignantMaui.Controls;
using MathomicsEnseignantMaui.Listener;
using MathomicsEnseignantMaui.Models;
using MathomicsEnseignantMaui.ViewModels;
using MathomicsEnseignantMaui.Views.SubViews.PupilCarousel;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.Controls.Xaml;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
using System.Diagnostics;
namespace MathomicsEnseignantMaui.Views.SubViews
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CompetenceSelector: StackLayout
{
public SetCompetenceListener SetCompetenceListener { get; set; } = null;
public static readonly BindableProperty PupilProperty = BindableProperty.Create(nameof(Pupil), typeof(Pupil), typeof(CompetenceSelector), null);
public Pupil Pupil
{
get { return (Pupil)GetValue(PupilProperty); }
set
{
if(value == null)
{
Console.WriteLine("CarouselCompetencesView", "ONCOMPETENCESELECTOR SET PUPIL IS NULL");
}
SetValue(PupilProperty, value);
}
}
public static readonly BindableProperty ClasseProperty = BindableProperty.Create(nameof(Classe), typeof(Classe), typeof(CompetenceSelector), null);
public Classe Classe
{
get { return (Classe)GetValue(ClasseProperty); }
set
{
SetValue(ClasseProperty, value);
}
}
public static readonly BindableProperty CarouselProperty = BindableProperty.Create(nameof(Carousel), typeof(CarouselView), typeof(CompetenceSelector), null, propertyChanged : InitCarouselOnChange);
public CarouselView Carousel
{
get { return (CarouselView)GetValue(CarouselProperty); }
set { SetValue(CarouselProperty, value); }
}
private static void InitCarouselOnChange(BindableObject view, object oldValue, object newValue)
{
CompetenceSelector _view = (CompetenceSelector)view;
_view.Carousel = (CarouselView)newValue;
if (_view.Carousel != null )
{
_view.Carousel.ScrollTo(0);
_view.IsScrolling = true;
_view.Carousel.Scrolled += (e, v) => {
Console.WriteLine("COMPETENCE SELECTOR - is SCROLLING END START METHOD - IS DRFAGGED" + _view.Carousel.IsDragging);
ObservableCollection<ITitled> entries = (ObservableCollection<ITitled>)_view.Carousel.ItemsSource;
int currentIndex = entries.IndexOf((ITitled)_view.Carousel.CurrentItem);
_view._selectorAllCompetences.IsChecked = (currentIndex == 0);
// _view.Carousel.SetIsDragging(false);
Console.WriteLine("COMPETENCE SELECTOR - is SCROLLING END END METHOD - IS DRFAGGED" + _view.Carousel.IsDragging);
_view.IsScrolling = false;
};
_view.Carousel.CurrentItemChanged += (e, v) =>
{
Console.WriteLine("COMPETENCE SELECTOR - CurrentItemChanged END METHOD - IS DRFAGGED" + _view.Carousel.IsDragging);
Console.WriteLine("Position: " + _view.Carousel.Position);
};
// CurrentItemChanged = "CarouselViewItemChanged"
}
}
private bool IsScrolling = false ;
public CompetenceSelector()
{
InitializeComponent();
// BindingContext = this;
}
public void NextCompetence(object sender, EventArgs args)
{
if ( !Scrolling())
{
ObservableCollection<ITitled> entries = (ObservableCollection<ITitled>)Carousel.ItemsSource;
ITitled currentItem = (ITitled)this.Carousel.CurrentItem;
int currentIndex = entries.IndexOf((ITitled)this.Carousel.CurrentItem);
int nextIndex = (currentIndex + 1) % entries.Count();
ITitled nextItem = entries[nextIndex];
setPositionCarousel(currentIndex, nextIndex, entries);
this._selectorAllCompetences.IsChecked = (nextIndex == 0);
this._selectorAllCompetences.IsEnabled = true;
}
}
public void SetCompetence(String title, bool animate = true)
{
ObservableCollection<ITitled> entries = (ObservableCollection<ITitled>)Carousel.ItemsSource;
foreach(ITitled entry in entries)
{
if(entry.Name == title)
{
// this.Carousel.ScrollTo(entries.IndexOf(entry), animate);
if (animate) { this.Carousel.ScrollTo(entries.IndexOf(entry)); }
else {
this.Carousel.CurrentItem = entry;
}
break;
}
}
}
private void setPositionCarousel(int currentIndex, int nextIndex, ObservableCollection<ITitled> entries)
{
IsScrolling = true;
// this.Carousel.SetIsDragging(true);
Console.WriteLine("COMPETENCE SELECTOR - is SCROLLING START - IS DRFAGGED" + Carousel.IsDragging);
this.Carousel.ScrollTo(nextIndex);
Console.WriteLine("COMPETENCE SELECTOR - is SCROLLING START AFTER SCROLL TO - IS DRAGGED" + Carousel.IsDragging);
if (SetCompetenceListener != null)
{
SetCompetenceListener.Action(entries[nextIndex]);
}
}
private int GetIndexOfCurrentItem()
{
ObservableCollection<ITitled> entries = (ObservableCollection<ITitled>)Carousel.ItemsSource;
ITitled currentItem = (ITitled)this.Carousel.CurrentItem;
return entries.IndexOf((ITitled)this.Carousel.CurrentItem);
}
public void PreviousCompetence(object sender, EventArgs args)
{
if (!Scrolling()) {
ObservableCollection<ITitled> entries = (ObservableCollection<ITitled>)Carousel.ItemsSource;
int currentIndex = GetIndexOfCurrentItem();
int nextIndex = currentIndex - 1 < 0 ? entries.Count() - 1 : currentIndex - 1;
ITitled nextItem = (ITitled)entries[nextIndex];
setPositionCarousel(currentIndex, nextIndex, entries);
this._selectorAllCompetences.IsChecked = (nextIndex == 0);
this._selectorAllCompetences.IsEnabled = true;
}
}
public void ReturnToAllCompetences()
{
if(this.Carousel != null) {
if(GetIndexOfCurrentItem() != 0 && !Scrolling()) {
IsScrolling = true;
this.Carousel.ScrollTo(0);
if(SetCompetenceListener != null)
{
SetCompetenceListener.Action(null);
}
}
}
}
private bool Scrolling()
{
return IsScrolling || Carousel.IsDragging;
}
void OnCheckBoxCheckedChanged(object sender, CheckedChangedEventArgs e)
{
if (e.Value)
{
ReturnToAllCompetences();
}
}
async void PushModalPage(object sender,EventArgs args)
{
var competencesListPage = new TreeCarouselView(this.Classe, this.Pupil);
await Navigation.PushModalAsync(competencesListPage);
}
}
}
As you see, I try to set IsScrolling when I start scrolling and stop it when I call the scrolled event. But it does not work.
Here is the view in which I call this function:
<?xml version="1.0" encoding="utf-8"?>
<StackLayout
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MathomicsEnseignantMaui.Views.SubViews.CarouselCompetences.CarouselCompetencesView"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
xmlns:controls="clr-namespace:MathomicsEnseignantMaui.Views.Controls;assembly=MathomicsEnseignantMaui"
xmlns:microcharts="clr-namespace:Microcharts2;assembly=MathomicsEnseignantMaui"
xmlns:subviews="clr-namespace:MathomicsEnseignantMaui.Views.SubViews;assembly=MathomicsEnseignantMaui"
x:Name="MyCarouselCompetence"
>
<subviews:TitleView
x:Name="_titleView"
Title="{Binding classe.tag, StringFormat='Progression - {0:F0}', Source={x:Reference MyCarouselCompetence}}"
HorizontalOptions="CenterAndExpand" Margin="10,0,10,0"
HeightRequest="70"/>
<Grid RowDefinitions="5*,1*"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<CarouselView
Grid.Row="0"
x:Name="_CarouselCompetencesView"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Scrolled="OnSwiped"
IsBounceEnabled="True"
ItemsSource="{Binding ContentViews, Source={x:Reference MyCarouselCompetence}}"
>
<CarouselView.ItemTemplate>
<DataTemplate >
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" RowDefinitions="1*,1*" >
<ContentView Grid.Row="0" Padding="50,10,50,0" BackgroundColor="{StaticResource backgroundColor}" Margin="0,0,0,5">
<ContentView Content="{Binding content}"/>
</ContentView>
<ContentView Grid.Row="1" Content="{Binding curveContent}" Margin="0,0,0,5"/>
</Grid>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<subviews:CompetenceSelector
x:Name="_competenceSelector"
Carousel="{Binding ., Source={x:Reference _CarouselCompetencesView}}"
Grid.Row="1" BackgroundColor="{StaticResource backgroundColor}"
Classe="{Binding classe, Source={x:Reference MyCarouselCompetence}, Mode=TwoWay}"
/>
</Grid>
</StackLayout>
A view with SKCanvasView is called here in the datatemplate
ContentView Content="{Binding content}"/>
I want scrolling to happen without crashing the application. Even when I scroll very quickly.
Thank you for your help.