maui icon indicating copy to clipboard operation
maui copied to clipboard

Issue with ListView and ObservableCollection

Open gavinb80 opened this issue 3 years ago • 7 comments

Description

When using a ObservableCollection in my project and displaying via a ListView, I have an issue when clearing the content of the ObservableCollection.

When calling incidentList.Clear(); it works fine on Android, but on iOS and MacOS I get the error "Object reference not set to an instance of an object."

Steps to Reproduce

code used that generates error:

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DsfrsApp.Models;
using Newtonsoft.Json;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Xml;

namespace DsfrsApp.ViewModel;

public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    ObservableCollection<IncidentCategoryModel> incidentCategories;

    [ObservableProperty]
    ObservableCollection<Item> items;

    [ObservableProperty]
    bool listRefreshing = false;

    public RelayCommand ReloadTaskCommand { get; }

    public MainViewModel()
    {
        ReloadTaskCommand = new RelayCommand(GetIncidents);
        incidentCategories = new ObservableCollection<IncidentCategoryModel>();
        items = new ObservableCollection<Item>();
    }

    async void GetIncidents()
    { 
        await getIncidentDetails("https://www.somesite.com/rss-feed.xml");
    }

    async Task getIncidentDetails(string feedUrl)
    {

        ListRefreshing = true;

        HttpClient client = new()
        {
            Timeout = new TimeSpan(0, 0, 20)
        };

        client.DefaultRequestHeaders.UserAgent.Clear();
        client.DefaultRequestHeaders.Add("User-Agent", "DsfrAppTest");

        await Task.Run(() =>
        {
            using var webResponse =  client.GetAsync(feedUrl, HttpCompletionOption.ResponseHeadersRead);
            using var downloadStream = webResponse.Result.Content.ReadAsStreamAsync().Result;

            StreamReader reader = new StreamReader(downloadStream);
            string xmlData = reader.ReadToEnd();
            XmlDocument xmlDocument = new();

            xmlDocument.LoadXml(xmlData);
            var jsonText = JsonConvert.SerializeXmlNode(xmlDocument);

            var incidents = Incident.FromJson(jsonText);
            Items.Clear();

            foreach (var incident in incidents.Rss.Channel.Item.Take(500))
            {
                Items.Add(incident);
            }

            var catDetail =
                from item in Items
                group item by item.Title into catList
                select new
                {
                    Title = catList.Key,
                    Count = catList.Count()
                };

            IncidentCategories.Clear();
            ListRefreshing = false;

            foreach (var cat in catDetail)
            {
                IncidentCategories.Add(new IncidentCategoryModel()
                {
                    ItemCount = cat.Count,
                    Title = cat.Title
                });
            }
        });
    }
}

MainView.Xml

<?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"
             x:Class="DsfrsApp.MainPage"
             xmlns:viewmodel="clr-namespace:DsfrsApp.ViewModel"
             x:DataType="viewmodel:MainViewModel"
             xmlns:models="clr-namespace:DsfrsApp.Models">
			 
        <StackLayout 
            Padding="8"
            HorizontalOptions="FillAndExpand"
            VerticalOptions="FillAndExpand">

            <Button 
                    x:Name="btnFetch"
                    Text="Fetch Data"
                    Command="{Binding ReloadTaskCommand}"
                    Margin="8"/>

            <ListView
                ItemsSource="{Binding IncidentCategories}"
                HasUnevenRows="True"
                VerticalOptions="FillAndExpand"
                ItemSelected="ListView_ItemSelected"
                IsPullToRefreshEnabled="True"
                RefreshCommand="{Binding ReloadTaskCommand}"
                IsRefreshing="{Binding ListRefreshing, Mode=TwoWay}">

                <ListView.ItemTemplate>
                    <DataTemplate
                        x:DataType="models:IncidentCategoryModel">
                        <ViewCell>
                            <Grid
                                Padding="8">

                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                </Grid.RowDefinitions>

                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>

                                <Rectangle
                                    WidthRequest="60"
                                    HeightRequest="60"
                                    BackgroundColor="lightblue"
                                    Margin="8"
                                    Grid.Column="0"
                                    Grid.RowSpan="2" />
                                   
                               
                                 <Label Grid.Column="1"
                                    Grid.Row="0"
                                    Text="{Binding Title}"
                                    FontAttributes="Bold"
                                    FontSize="Title"/>

                                 <Label Grid.Column="1"
                                    Grid.Row="1"
                                    Text="{Binding itemCountText}"
                                    FontSize="Subtitle" />

                            </Grid>
                        </ViewCell>

                    </DataTemplate>

                </ListView.ItemTemplate>

            </ListView>

        </StackLayout>
</ContentPage>

Version with bug

6.0.400-preview.22330.6

Last version that worked well

Unknown/Other

Affected platforms

iOS, macOS

Affected platform versions

iOS 15.5, macOS 12

Did you find any workaround?

No workaround found

Relevant log output

System.NullReferenceException: Object reference not set to an instance of an object.
  at at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.UnevenListViewDataSource.ClearPrototype()
  at at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.UnevenListViewDataSource.InvalidatingPrototypicalCellCache()
  at at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.ListViewDataSource.InvalidatePrototypicalCellCache()
  at at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.InvalidateCellCache()
  at at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.UpdateItems(NotifyCollectionChangedEventArgs e, Int32 section, Boolean resetWhenGrouped)
  at at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
  at at Microsoft.Maui.Controls.Internals.TemplatedItemsList`2[[Microsoft.Maui.Controls.ItemsView`1[[Microsoft.Maui.Controls.Cell, 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.Cell, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnCollectionChanged(NotifyCollectionChangedEventArgs e)
  at at Microsoft.Maui.Controls.Internals.TemplatedItemsList`2[[Microsoft.Maui.Controls.ItemsView`1[[Microsoft.Maui.Controls.Cell, 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.Cell, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnProxyCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
  at at Microsoft.Maui.Controls.ListProxy.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
  at at Microsoft.Maui.Controls.ListProxy.<>c__DisplayClass34_0.<OnCollectionChanged>b__0()
  at at Microsoft.Maui.Dispatching.Dispatcher.<>c__DisplayClass9_0.<DispatchImplementation>b__0()
  at at CoreFoundation.DispatchQueue.static_dispatcher_to_managed(IntPtr context)
  at at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass)
  at DsfrsApp.Program.Main(String[] args) in /Users/gavinbeard/Projects/DsfrsApp/DsfrsApp/Platforms/iOS/Program.cs:13

gavinb80 avatar Jul 14 '22 13:07 gavinb80

I see you mention 6.0 Release Candidate 3 specifically as a version, is that correct? What does the command dotnet --version tell you?

jfversluis avatar Jul 14 '22 13:07 jfversluis

Hi @gavinb80. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost avatar Jul 14 '22 13:07 ghost

I see you mention 6.0 Release Candidate 3 specifically as a version, is that correct? What does the command dotnet --version tell you?

Hi

Version is 6.0.400-preview.22330.6 which I believe is the latest

gavinb80 avatar Jul 14 '22 15:07 gavinb80

I believe that I am seeing the same issue when displaying a ReadOnlyObservableCollection in a ListView. In my case, the ROOC is exposed by the view model from an underlying DynamicData SourceList that is being filtered and update behind the scenes. The list loads just fine the first time, but I get this NullReferenceException when the view deactivates and the underlying DynamicData subscription is disposed.

System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.UnevenListViewDataSource.ClearPrototype()
   at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.UnevenListViewDataSource.InvalidatingPrototypicalCellCache()
   at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.ListViewDataSource.InvalidatePrototypicalCellCache()
   at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.InvalidateCellCache()
   at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.UpdateItems(NotifyCollectionChangedEventArgs e, Int32 section, Boolean resetWhenGrouped)
   at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at Microsoft.Maui.Controls.Internals.TemplatedItemsList`2[[Microsoft.Maui.Controls.ItemsView`1[[Microsoft.Maui.Controls.Cell, 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.Cell, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at Microsoft.Maui.Controls.Internals.TemplatedItemsList`2[[Microsoft.Maui.Controls.ItemsView`1[[Microsoft.Maui.Controls.Cell, 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.Cell, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnProxyCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at Microsoft.Maui.Controls.Internals.TemplatedItemsList`2[[Microsoft.Maui.Controls.ItemsView`1[[Microsoft.Maui.Controls.Cell, 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.Cell, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnItemsSourceChanged(Boolean fromGrouping)
   at Microsoft.Maui.Controls.Internals.TemplatedItemsList`2[[Microsoft.Maui.Controls.ItemsView`1[[Microsoft.Maui.Controls.Cell, 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.Cell, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].BindableOnPropertyChanged(Object sender, PropertyChangedEventArgs e)
   at Microsoft.Maui.Controls.BindableObject.OnPropertyChanged(String propertyName)
   at Microsoft.Maui.Controls.Element.OnPropertyChanged(String propertyName)
   at Microsoft.Maui.Controls.BindableObject.SetValueActual(BindableProperty property, BindablePropertyContext context, Object value, Boolean currentlyApplying, SetValueFlags attributes, Boolean silent)
   at Microsoft.Maui.Controls.BindableObject.SetValueCore(BindableProperty property, Object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes)
   at Microsoft.Maui.Controls.BindingExpression.ApplyCore(Object sourceObject, BindableObject target, BindableProperty property, Boolean fromTarget)
   at Microsoft.Maui.Controls.BindingExpression.Apply(Boolean fromTarget)
   at Microsoft.Maui.Controls.BindingExpression.BindingExpressionPart.<PropertyChanged>b__49_0()
   at Microsoft.Maui.Controls.DispatcherExtensions.DispatchIfRequired(IDispatcher dispatcher, Action action)
   at Microsoft.Maui.Controls.BindingExpression.BindingExpressionPart.PropertyChanged(Object sender, PropertyChangedEventArgs args)

warpedgeoid avatar Jul 19 '22 17:07 warpedgeoid

Hi @gavinb80. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md

This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost avatar Jul 21 '22 09:07 ghost

@PureWeen has commented on Discord that this is likely a regression that was previously fixed in Xamarin Forms and related to the following line missing a ? after element. https://github.com/dotnet/maui/blob/64432c450080efa99084f040c933c9011b066989/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ListViewRenderer.cs#L964

I'm going to try to find time to do a repro this weekend.

warpedgeoid avatar Jul 21 '22 21:07 warpedgeoid

Repo on 6.0.401 on MacOS [edit: still a problem in 7.0.100 rc1] https://github.com/mike3sullivan/MauiListViewHasUnevenRowsItemTemplate

Specifically, use of HasUnevenRows=True in combination with an ItemTemplate.

In Main.xaml.cs, exception caught in Reset_Clicked (ObservableCollection .Clear()'d) results in error: System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRenderer.UnevenListViewDataSource.ClearPrototype() at Microsoft.Maui.Controls.Handlers.Compatibility.ListViewRe…

mike3sullivan avatar Sep 16 '22 19:09 mike3sullivan

Still seems to be ab issue in rc2

gavinb80 avatar Oct 17 '22 21:10 gavinb80

@gavinb80 RC2 of .NET MAUI hasn't been released yet. It will release with Preview 4

PureWeen avatar Oct 17 '22 22:10 PureWeen

Now resolved with Preview 4

gavinb80 avatar Oct 19 '22 18:10 gavinb80