Skip to content

[Android] CarouselView: VirtualView cannot be null here, when clearing and adding items on second navigation #22035

Closed

Description

Description

In Android only, starting from Maui 8.0.6 SR1 and continues to at least 8.0.20 SR4 (might be in 8.0.21 SR4.1 but haven't tested that one EDIT: it is still in 8.0.21 SR4.1), an exception occurs when adding items to the ObservableCollection backing a CarouselView after having navigated to the same page for a second time. This is very similar to what used to happen to Collection Views in .NET 7 as chronicled in #12219 (and fixed in #13385), except now it happens for CarouselViews. Case-in-point, my repro project is merely an updated and modified version of jaldinger's repro project from back in that issue, and it manifests the same behavior for CarouselViews now as it did for CollectionViews back then (I have verified that CollectionViews do not experience this issue anymore, so the previous bugfix hasn't been regressed).

carousel_virtualview_error.mp4

Occurs in 8.0.21 SR 4.1, 8.0.20 SR4, 8.0.14 SR3.1, 8.0.10 SR3, 8.0.7 SR2, Might be in the newest 8.0.21 SR 4.1 but haven't tested that one yet since it just came out hours ago. It is tested now

Steps to Reproduce

  1. Create a File > New .NET MAUI App
  2. Add a second ContentPage to the project, named "Page2"
  3. In AppShell.xaml.cs add: Routing.RegisterRoute("Page2", typeof(Page2));
  4. In MauiProgram.cs add: builder.Services.AddScoped();
  5. Add a class to the project called "Foo" and populate it as follows:
public class Foo
{
    public string ImagePath { get; set; }
}
  1. Copy all the images from here and paste them in your project's Resources/Images folder
  2. In Page2.xaml replace all the content with:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MauiAppRepro1"
             x:Class="MauiAppRepro1.Page2"
             x:DataType="local:Page2"
             Title="Page2"
             x:Name="this"
             BindingContext="{x:Reference this}">
    <VerticalStackLayout>
        <CarouselView ItemsSource="{Binding FooImages}" MaximumHeightRequest="150">
            <CarouselView.ItemTemplate>
                <DataTemplate x:DataType="local:Foo">
                    <VerticalStackLayout>
                        <Image Source="{Binding ImagePath}" />
                    </VerticalStackLayout>
                </DataTemplate>
            </CarouselView.ItemTemplate>
        </CarouselView>

        <Button
            Text="Load items"
            Pressed="Button_Pressed" />

    </VerticalStackLayout>
</ContentPage>
  1. In Page2.xaml.cs replace the class with:
public partial class Page2 : ContentPage
{
	public ObservableCollection<Foo> FooImages { get; set; } = new();

	public Page2()
	{
		InitializeComponent();

		this.BindingContext = this;
       }

	private async void Button_Pressed(object sender, EventArgs e)
	{
		FooImages.Clear();

		await Task.Delay(1000);

		FooImages.Add(new Foo { ImagePath = "red_mm.png" });
		FooImages.Add(new Foo { ImagePath = "yellow_mm.png" });
		FooImages.Add(new Foo { ImagePath = "blue_mm.png" });
		FooImages.Add(new Foo { ImagePath = "orange_mm.png" });
    }
}
  1. In MainPage.xaml.cs modify OnCounterClicked as follows:
private async void OnCounterClicked(object sender, EventArgs e)
{
	count++;

	if (count == 1)
		CounterBtn.Text = $"Clicked {count} time";
	else
		CounterBtn.Text = $"Clicked {count} times";

	SemanticScreenReader.Announce(CounterBtn.Text);

    await Shell.Current.GoToAsync("/Page2");
}
  1. Run the app on Android
  2. Click the button on MainPage
  3. Click the button on Page2 two or more times --> works fine
  4. Go back to MainPage
  5. Click the button on MainPage again
  6. Click the button on Page2 --> Exception occurs: **System.InvalidOperationException:** 'VirtualView cannot be null here'

Link to public reproduction project repository

https://github.com/RedZone908/CarouselVirtualViewErrorRepro/tree/master

Version with bug

8.0.6 SR1

Is this a regression from previous behavior?

Yes, this used to work in .NET MAUI

Last version that worked well

8.0.3 GA

Affected platforms

Android

Affected platform versions

Android 21 and up

Did you find any workaround?

Simply instantiating a new ObservableCollection in the page's OnAppearing handler instead of reusing the existing one will not trigger the error.

Relevant log output

**System.InvalidOperationException:** 'VirtualView cannot be null here'

[libc] Requested dump for pid 17324 (e.mauiapprepro1)
**System.InvalidOperationException:** 'VirtualView cannot be null here'

[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: VirtualView cannot be null here
[mono-rt]    at Microsoft.Maui.Handlers.ViewHandler`2[[Microsoft.Maui.Controls.CarouselView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[AndroidX.RecyclerView.Widget.RecyclerView, Xamarin.AndroidX.RecyclerView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].get_VirtualView() in D:\a\_work\1\s\src\Core\src\Handlers\View\ViewHandlerOfT.cs:line 42
[mono-rt]    at Microsoft.Maui.Controls.Handlers.Items.CarouselViewHandler.CreateAdapter() in D:\a\_work\1\s\src\Controls\src\Core\Handlers\Items\CarouselViewHandler.Android.cs:line 17
[mono-rt]    at Microsoft.Maui.Controls.Handlers.Items.MauiRecyclerView`3[[Microsoft.Maui.Controls.CarouselView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Controls.Handlers.Items.ItemsViewAdapter`2[[Microsoft.Maui.Controls.CarouselView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Controls.Handlers.Items.IItemsViewSource, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Controls.Handlers.Items.IItemsViewSource, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].UpdateAdapter() in D:\a\_work\1\s\src\Controls\src\Core\Handlers\Items\Android\MauiRecyclerView.cs:line 236
[mono-rt]    at Microsoft.Maui.Controls.Handlers.Items.MauiCarouselRecyclerView.UpdateAdapter() in D:\a\_work\1\s\src\Controls\src\Core\Handlers\Items\Android\MauiCarouselRecyclerView.cs:line 140
[mono-rt]    at Microsoft.Maui.Controls.Handlers.Items.MauiCarouselRecyclerView.CollectionItemsSourceChanged(Object sender, NotifyCollectionChangedEventArgs e) in D:\a\_work\1\s\src\Controls\src\Core\Handlers\Items\Android\MauiCarouselRecyclerView.cs:line 251
[mono-rt]    at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.CollectionChanged(NotifyCollectionChangedEventArgs args) in D:\a\_work\1\s\src\Controls\src\Core\Handlers\Items\Android\ItemsSources\ObservableItemsSource.cs:line 131
[mono-rt]    at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.<>c__DisplayClass33_0.<CollectionChanged>b__0() in D:\a\_work\1\s\src\Controls\src\Core\Handlers\Items\Android\ItemsSources\ObservableItemsSource.cs:line 106
[mono-rt]    at Microsoft.Maui.Controls.DispatcherExtensions.DispatchIfRequired(IDispatcher dispatcher, Action action) in D:\a\_work\1\s\src\Controls\src\Core\DispatcherExtensions.cs:line 59
[mono-rt]    at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.CollectionChanged(Object sender, NotifyCollectionChangedEventArgs args) in D:\a\_work\1\s\src\Controls\src\Core\Handlers\Items\Android\ItemsSources\ObservableItemsSource.cs:line 106
[mono-rt]    at Microsoft.Maui.Controls.WeakNotifyCollectionChangedProxy.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) in D:\a\_work\1\s\src\Controls\src\Core\Internals\WeakEventProxy.cs:line 78
[mono-rt]    at Microsoft.Maui.Controls.MarshalingObservableCollection.OnCollectionChanged(NotifyCollectionChangedEventArgs args) in D:\a\_work\1\s\src\Controls\src\Core\Items\MarshalingObservableCollection.cs:line 49
[mono-rt]    at Microsoft.Maui.Controls.MarshalingObservableCollection.Reset(NotifyCollectionChangedEventArgs args) in D:\a\_work\1\s\src\Controls\src\Core\Items\MarshalingObservableCollection.cs:line 152
[mono-rt]    at Microsoft.Maui.Controls.MarshalingObservableCollection.HandleCollectionChange(NotifyCollectionChangedEventArgs args) in D:\a\_work\1\s\src\Controls\src\Core\Items\MarshalingObservableCollection.cs:line 85
[mono-rt]    at Microsoft.Maui.Controls.MarshalingObservableCollection.<>c__DisplayClass8_0.<InternalCollectionChanged>b__0() in D:\a\_work\1\s\src\Controls\src\Core\Items\MarshalingObservableCollection.cs:line 65
[mono-rt]    at Microsoft.Maui.Controls.DispatcherExtensions.DispatchIfRequired(IDispatcher dispatcher, Action action) in D:\a\_work\1\s\src\Controls\src\Core\DispatcherExtensions.cs:line 59
[mono-rt]    at Microsoft.Maui.Controls.MarshalingObservableCollection.InternalCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args) in D:\a\_work\1\s\src\Controls\src\Core\Items\MarshalingObservableCollection.cs:line 65
[mono-rt]    at System.Collections.ObjectModel.ObservableCollection`1[[MauiAppRepro1.Foo, MauiAppRepro1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnCollectionChanged(NotifyCollectionChangedEventArgs e)
[mono-rt]    at System.Collections.ObjectModel.ObservableCollection`1[[MauiAppRepro1.Foo, MauiAppRepro1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnCollectionReset()
[mono-rt]    at System.Collections.ObjectModel.ObservableCollection`1[[MauiAppRepro1.Foo, MauiAppRepro1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].ClearItems()
[mono-rt]    at System.Collections.ObjectModel.Collection`1[[MauiAppRepro1.Foo, MauiAppRepro1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].Clear()
[mono-rt]    at MauiAppRepro1.Page2.Button_Pressed(Object sender, EventArgs e) in C:\repos\GitIssues\MauiAppRepro1.App\Page2.xaml.cs:line 18
[mono-rt]    at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)
[mono-rt]    at Android.App.SyncContext.<>c__DisplayClass2_0.<Post>b__0() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.App/SyncContext.cs:line 36
[mono-rt]    at Java.Lang.Thread.RunnableImplementor.Run() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Java.Lang/Thread.cs:line 36
[mono-rt]    at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Java.Lang.IRunnable.cs:line 84
[mono-rt]    at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:line 26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Type

No type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions