Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
RedZone908 opened this issue Apr 24, 2024 · 11 comments · Fixed by #22207
Assignees
Labels
area-controls-collectionview CollectionView, CarouselView, IndicatorView fixed-in-8.0.40 i/regression This issue described a confirmed regression on a currently supported version platform/android 🤖 s/needs-attention Issue has more information and needs another look s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Milestone

Comments

@RedZone908
Copy link

RedZone908 commented Apr 24, 2024

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
@RedZone908 RedZone908 added the t/bug Something isn't working label Apr 24, 2024
@RedZone908
Copy link
Author

I just updated the repro project to 8.0.21 SR 4.1 and confirmed the bug still exists there.

@Zhanglirong-Winnie Zhanglirong-Winnie added s/verified Verified / Reproducible Issue ready for Engineering Triage s/triaged Issue has been reviewed i/regression This issue described a confirmed regression on a currently supported version labels Apr 25, 2024
@Zhanglirong-Winnie
Copy link
Collaborator

Verified this issue with Visual Studio 17.10.0 Preview 5(8.0.21&8.0.20&8.0.14&8.0.10&8.0.7&8.0.40-nightly.10515+sha.d6a3c50c93-azdo.9437127). Can repro on android platform with sample project.
8.0.3 works fine.

@PureWeen PureWeen added this to the .NET 8 SR6 milestone Apr 26, 2024
@Eilon Eilon added the area-controls-collectionview CollectionView, CarouselView, IndicatorView label Apr 26, 2024
@plppp2001
Copy link

I concur, this is happening in my carouselview. Please fix.

@SarthakB26
Copy link

+1

@PureWeen
Copy link
Member

PureWeen commented May 3, 2024

Can you test with the latest nightly build?
https://github.com/dotnet/maui/wiki/Nightly-Builds

@PureWeen PureWeen added the s/needs-info Issue needs more info from the author label May 3, 2024
@RedZone908
Copy link
Author

I will test

@dotnet-policy-service dotnet-policy-service bot added s/needs-attention Issue has more information and needs another look and removed s/needs-info Issue needs more info from the author labels May 3, 2024
@RedZone908
Copy link
Author

@PureWeen Unfortunately, with the latest nightly 8.0.40-nightly.10602, this bug is still present in the repro project. Did a full clean and build. See below.

carousel_virtualview_error_8.0.40-nightly.10602.mp4

@PureWeen
Copy link
Member

PureWeen commented May 3, 2024

If you make Page2 Transient instead of scoped does that fix it?

https://github.com/RedZone908/CarouselVirtualViewErrorRepro/blob/master/MauiProgram.cs#L18

@RedZone908
Copy link
Author

RedZone908 commented May 3, 2024

@PureWeen that takes away the error, but, it also makes the CarouselView start over with zero items at each page load (which makes sense, since the page is being recreated), so it's still not technically a return to the 8.0.3 behavior where the carousel items could be retained between page loads before adding/deleting any other items.

@PureWeen
Copy link
Member

PureWeen commented May 3, 2024

@PureWeen that takes away the error, but, it also makes the CarouselView start over with zero items at each page load (which makes sense, since the page is being recreated), so it's still not technically a return to the 8.0.3 behavior where the carousel items could be retained between page loads before adding/deleting any other items.

can you make the viewmodel and data survive longer than the scope of the page?

to be clear I'm not saying this isn't a bug :-) Just proposing some workarounds for now

@RedZone908
Copy link
Author

Ah, gotcha! Well, for the actual proprietary company app where we encountered this bug, we actually didn't need to retain the data so our workaround was to simply instantiate a new ObservableCollection on every page load, and that did take away the error. So this particular bug definitely isn't a blocker for the product I support at this stage. It'd be nice if we didn't have to do it, sure, but it IS an easy one-line workaround, basically:

public override void OnAppearing()
{
    this.MyListOfThings = new ObservableCollection() { /*your data here */ };
    // can also call a method on your viewModel to do this -- just showing as OnAppearing to keep the example simple
}

I was merely pointing out the "can't retain data" piece because at this point, I was thinking beyond the scope of my application and to the broader implications of the bug itself. But yeah, on Monday I can give the repro project a longer-lived dataset. If that works, then that also would be a viable workaround for others.

kubaflo added a commit to kubaflo/maui that referenced this issue May 3, 2024
@PureWeen PureWeen self-assigned this May 6, 2024
@jsuarezruiz jsuarezruiz self-assigned this May 7, 2024
@PureWeen PureWeen modified the milestones: .NET 8 SR6, .NET 8 SR5 May 10, 2024
PureWeen added a commit that referenced this issue May 10, 2024
…g and adding items on second navigation - fix (#22207)

* Fix #22035

* Added UITest

* Updated test

* Updated test

* Updated test

* Updated test

* - small change to code and fix test

* Update Issue22035.cs

* Update Issue22035.cs

* - fix test

* - fix test

---------

Co-authored-by: Javier Suárez <javiersuarezruiz@hotmail.com>
Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
Co-authored-by: Shane Neuville <shane94@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-controls-collectionview CollectionView, CarouselView, IndicatorView fixed-in-8.0.40 i/regression This issue described a confirmed regression on a currently supported version platform/android 🤖 s/needs-attention Issue has more information and needs another look s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Projects
Status: Done
8 participants