microsoft / microsoft/microsoft-ui-xaml

Crash when PipsPager and FlipView are bound to same ObservableCollection and deleting 2 or more items

Open
#11,495 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area-PipsPager bug team-Controls
Dominant language
C++
Stars
8.4k
Forks
942
Avg merge
2d 7h
Merged PRs (30d)
105

Description

### Describe the bug

WinUI 3 Gallery app (https://github.com/microsoft/WinUI-Gallery) contains an example of a PipsPager integrated with a FlipView. When you adapt the example to become responsive to changes from the ObservableCollection, it all works fine when you delete 1 item from the collection. When deleting two or more, a crash occurs which cannot be explained from the C# side, nor caught by the debugger

### Why is this important?

Either PipsPager alone or FlipView alone doesn't produce a crash, it crashes only when the two are used on the same ObservableCollection and deleting 2 or more items. It is a crash for no apparent reason.

### Steps to reproduce the bug

Create a new WinUI project called `WinUI3Crash` and perform replacements underneath.

Scenario 1: PipsPager and FlipView with tight databinding, click on Delete 2 button underneath, crash
Scenario 2: Using a SelectedIndex to see how PipsPager perhaps can go beyond boundary, crash
Scenario 3: same as Scenario 2, just only FlipView, no crash
Scenario 4: same as Scenario 2, just only PipsPager, no crash
Scenario 5: Using NumberOfPages, crash
Scenario 6: same as Scenario 5, deferred updating of NumberOfPages, no crash

I'm using Scenario 6 as a workaround for the crash happening in production code, it would be great to know what the cause is, whether it requires a different fix, and whether it requires a fix at your end.

Code replacements:

Update TargetFramework within csproj to `net10.0-windows10.0.19041.0`

Update the following in case they differ:
```




```

Replace MainWindow.xaml
```







Delete 2 items, crash











Using SelectedIndex2, crash











Only FlipView, no crash










Only PipsPager, no crash




Using NumberOfPages5, crash











Update NumberOfPages6 in DispatcherQueue, no crash












```

Replace MainWindow.xaml.cs
```
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace WinUI3Crash
{
///
/// An empty window that can be used on its own or navigated to within a Frame.
///
public sealed partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public ObservableCollection Pictures1 = ["1", "2", "3", "4"];
public ObservableCollection Pictures2 = ["1", "2", "3", "4"];
public ObservableCollection Pictures3 = ["1", "2", "3", "4"];
public ObservableCollection Pictures4 = ["1", "2", "3", "4"];
public ObservableCollection Pictures5 = ["1", "2", "3", "4"];
public ObservableCollection Pictures6 = ["1", "2", "3", "4"];

public int SelectedIndex2
{
get;
set
{
if (field != value)
{
field = value;
OnPropertyChanged();
}
}
}

public int SelectedIndex3
{
get;
set
{
if (field != value)
{
field = value;
OnPropertyChanged();
}
}
}

public int SelectedIndex4
{
get;
set
{
if (field != value)
{
field = value;
OnPropertyChanged();
}
}
}

public int NumberOfPages5
{
get;
set
{
if (field != value)
{
field = value;
OnPropertyChanged();
}
}
}

public int NumberOfPages6
{
get;
set
{
if (field != value)
{
field = value;
OnPropertyChanged();
}
}
}

public MainWindow()
{
Pictures5.CollectionChanged += Pictures5_CollectionChanged;
Pictures6.CollectionChanged += Pictures6_CollectionChanged;
NumberOfPages5 = Pictures5.Count;
NumberOfPages6 = Pictures6.Count;
InitializeComponent();
}

private void Pictures5_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NumberOfPages5 = Pictures5.Count;
}

private void Pictures6_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
DispatcherQueue.TryEnqueue(() =>
{
NumberOfPages6 = Pictures6.Count;
});
}

private void Button_Click_2(object sender, RoutedEventArgs e)
{
if (sender is not Button button)
return;
if (button.Tag is not string collectionItem)
return;
ObservableCollection collection;
switch (collectionItem)
{
case "1":
collection = Pictures1;
break;
case "2":
collection = Pictures2;
break;
case "3":
collection = Pictures3;
break;
case "4":
collection = Pictures4;
break;
case "5":
collection = Pictures5;
break;
case "6":
collection = Pictures6;
break;
default:
return;
}
if (collection.Count < 2)
{
return;
}
collection.RemoveAt(collection.Count - 1);
collection.RemoveAt(collection.Count - 1);
}
}
}
```

### Actual behavior

Crashes inexplicably, also doesn't crash somewhat inexplicably when deferring NumberOfPages field update.

When using a SelectedIndex field, there is nothing which would update the SelectedIndex beyond the PipsPager boundary.

As a last resort, I did an AI analysis and it came up with:
```
Defer NumberOfPages update to allow FlipView to process the collection change first
This avoids a race condition in the WinUI framework between FlipView and PipsPager
```
Still it wouldn't make sense that there even is a race condition between the two controls.

### Expected behavior

PipsPager/FlipView combination should not become instable when deleting 2 or more items from a shared ObservableCollection

### Screenshots

Screenshot of the crash with full debugging options enabled, still not able to step into anything.

Image

### NuGet package version

Microsoft.WindowsAppSDK 2.3.1

### Windows version

Windows 11 (25H2): Build 26200

### Additional context

_No response_

Contributor guide

Open the contributing guide

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.

Research direction

Start with the replacement MainWindow.xaml and MainWindow.xaml.cs, build the supplied WinUI 3 repro, and run scenarios 1–6 to compare the crashing and non-crashing bindings. Trace the shared ObservableCollection updates between FlipView and PipsPager; done means the cause is identified and the multiple-item deletion case has a verified fix or a confirmed framework limitation.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.