App crashes on rapid async updates when using ToNotifyCollectionChanged with WinUI 3 ListView

Open
#115 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
55/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Quiet
Tech stack
csharp
Domain
desktop

Research direction

Start by running the WinUI 3 reproduction in MainWindow with ToNotifyCollectionChanged and SynchronizationContextCollectionEventDispatcher.Current, then inspect the dispatcher and collection synchronization path implicated by the stack trace. Done means rapid background Add and RemoveAt operations no longer terminate the app and both collection-change actions can be observed.

Written by the indexing model from the issue text.

Description

Description

When using ToNotifyCollectionChanged to bind an ObservableList to a WinUI 3 ListView, the application crashes with an ArgumentOutOfRangeException during rapid asynchronous updates (e.g., inside Task.Run).

Observed Behavior
  • The application terminates immediately with a specific exception during collection synchronization.
  • Crucially, while CollectionChanged is subscribed to log the action, only the Add action is ever logged before the process crashes. The subsequent Remove action is never reached/logged.
  • This crash does not occur if the same operations are performed directly on the UI thread (without Task.Run).
Exception

System.ArgumentOutOfRangeException: This collection cannot work with indices larger than Int32.MaxValue - 1 (0x7FFFFFFF - 1). (Parameter 'index')

Stack Trace
   at ABI.System.Collections.IList.ToAbiHelper.EnsureIndexInt32(UInt32 index, Int32 listCapacity)
   at ABI.System.Collections.IList.ToAbiHelper.GetAt(UInt32 index)
   at ABI.System.Collections.IList.Do_Abi_GetAt_0(IntPtr thisPtr, UInt32 index, IntPtr* result)
--- End of stack trace from previous location ---
   at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|38_0(Int32 hr)
   at WinRT.ExceptionHelpers.ThrowExceptionForHR(Int32 hr)
   at ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler.NativeDelegateWrapper.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at ObservableCollections.FiltableSynchronizedViewList`2.RaiseChangedEvent(NotifyCollectionChangedEventArgs e)
   at ObservableCollections.CollectionEventDispatcherEventArgs.Invoke()
   at ObservableCollections.SynchronizationContextCollectionEventDispatcher.SendOrPostCallback(Object state)
   at Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.<>c__DisplayClass2_0.<Post>b__0()
Reproducible Code
<Window
    x:Class="ObservableCollectionsTests.MainWindow"
    xmlns="[http://schemas.microsoft.com/winfx/2006/xaml/presentation](http://schemas.microsoft.com/winfx/2006/xaml/presentation)"
    xmlns:x="[http://schemas.microsoft.com/winfx/2006/xaml](http://schemas.microsoft.com/winfx/2006/xaml)">
    <StackPanel>
        <Button Content="Trigger Crash" Click="Button_Click"/>
        <ListView ItemsSource="{x:Bind List}" />
    </StackPanel>
</Window>
using Microsoft.UI.Xaml;
using ObservableCollections;
using System.Diagnostics;
using System.Threading.Tasks;

namespace ObservableCollectionsTests;

public sealed partial class MainWindow : Window
{
    private ObservableList<string> _ol = [];
    public INotifyCollectionChangedSynchronizedViewList<string> List;

    public MainWindow()
    {
        this.List = this._ol.CreateView(x => x).ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current);
        
        this.List.CollectionChanged += (s, e) => {
            Debug.WriteLine(e.Action);
        };
        
        InitializeComponent();
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        await Task.Run(() =>
        {
            this._ol.Add("1");
            this._ol.RemoveAt(0);
        });
    }
}
Dominant language
C#
Stars
1k
Forks
73
Avg merge
1d 14h
Merged PRs (30d)
3

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Cysharp/ObservableCollections

All issues in Cysharp/ObservableCollections

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.