diff --git a/src/OneWare.ErrorList/BatchObservableCollection.cs b/src/OneWare.ErrorList/BatchObservableCollection.cs
new file mode 100644
index 000000000..b8ee57c1a
--- /dev/null
+++ b/src/OneWare.ErrorList/BatchObservableCollection.cs
@@ -0,0 +1,67 @@
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+
+namespace OneWare.ErrorList;
+
+///
+/// ObservableCollection that can suppress change notifications while a batch of mutations is applied and
+/// raises a single Reset afterwards.
+/// This is required for , which crashes
+/// (ArgumentOutOfRangeException in AdjustCurrencyForRemove) when an item that is filtered out of the view is
+/// removed while the current position is 0. Handling a Reset makes the view rebuild itself safely.
+///
+internal class BatchObservableCollection : ObservableCollection
+{
+ private bool _isDirty;
+ private int _suspendLevel;
+
+ public IDisposable BeginBatch()
+ {
+ _suspendLevel++;
+ return new BatchScope(this);
+ }
+
+ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ if (_suspendLevel > 0)
+ {
+ _isDirty = true;
+ return;
+ }
+
+ base.OnCollectionChanged(e);
+ }
+
+ protected override void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ if (_suspendLevel > 0) return;
+
+ base.OnPropertyChanged(e);
+ }
+
+ private void EndBatch()
+ {
+ if (_suspendLevel == 0) return;
+
+ _suspendLevel--;
+ if (_suspendLevel > 0 || !_isDirty) return;
+
+ _isDirty = false;
+ OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count)));
+ OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ private sealed class BatchScope(BatchObservableCollection owner) : IDisposable
+ {
+ private bool _disposed;
+
+ public void Dispose()
+ {
+ if (_disposed) return;
+ _disposed = true;
+ owner.EndBatch();
+ }
+ }
+}
diff --git a/src/OneWare.ErrorList/ViewModels/ErrorListViewModel.cs b/src/OneWare.ErrorList/ViewModels/ErrorListViewModel.cs
index 97bacff8d..7f25b75eb 100644
--- a/src/OneWare.ErrorList/ViewModels/ErrorListViewModel.cs
+++ b/src/OneWare.ErrorList/ViewModels/ErrorListViewModel.cs
@@ -22,7 +22,7 @@ public class ErrorListViewModel : ExtendedTool, IErrorService
{
public const string IconKey = "MaterialDesign.ErrorOutline";
- private readonly ObservableCollection _items = new();
+ private readonly BatchObservableCollection _items = new();
private readonly IMainDockService _mainDockService;
private readonly IProjectExplorerService _projectExplorerExplorerViewModel;
@@ -194,7 +194,11 @@ public void RegisterErrorSource(string source)
public void ClearFile(string filePath)
{
- ListEx.RemoveMany(_items, _items.Where(x => x.FilePath.EqualPaths(filePath)));
+ using (_items.BeginBatch())
+ {
+ ListEx.RemoveMany(_items, _items.Where(x => x.FilePath.EqualPaths(filePath)));
+ }
+
ErrorRefresh?.Invoke(this, filePath);
RefreshCountToggle();
}
@@ -204,7 +208,10 @@ public void Clear(string source)
var errors = _items.Where(x => x.Source == source).ToList();
var files = errors.Select(x => x.FilePath).Distinct();
- ListEx.RemoveMany(_items, errors);
+ using (_items.BeginBatch())
+ {
+ ListEx.RemoveMany(_items, errors);
+ }
foreach (var file in files) ErrorRefresh?.Invoke(this, file);
@@ -231,10 +238,13 @@ public IEnumerable GetErrorsForFile(string filePath)
///
public void RefreshErrors(IList errors, string source, string filePath)
{
- ListEx.RemoveMany(_items,
- _items.Where(x => x.FilePath.EqualPaths(filePath) && x.Source == source && !errors.Contains(x)));
+ using (_items.BeginBatch())
+ {
+ ListEx.RemoveMany(_items,
+ _items.Where(x => x.FilePath.EqualPaths(filePath) && x.Source == source && !errors.Contains(x)));
- foreach (var e in errors) Add(e);
+ foreach (var e in errors) Add(e);
+ }
ErrorRefresh?.Invoke(this, filePath);
RefreshCountToggle();
@@ -328,15 +338,23 @@ private void RefreshCountToggle()
public void Clear(IProjectRoot project)
{
- ListEx.RemoveMany(_items, _items.Where(x => x.Root == project));
+ using (_items.BeginBatch())
+ {
+ ListEx.RemoveMany(_items, _items.Where(x => x.Root == project));
+ }
+
ErrorRefresh?.Invoke(this, project);
RefreshCountToggle();
}
public void Clear(IProjectRoot project, string source)
{
- ListEx.RemoveMany(_items,
- _items.Where(x => x.Root == project && x.Source == source));
+ using (_items.BeginBatch())
+ {
+ ListEx.RemoveMany(_items,
+ _items.Where(x => x.Root == project && x.Source == source));
+ }
+
ErrorRefresh?.Invoke(this, project);
RefreshCountToggle();
}