stride3d/stride · error · ArgumentException
The list must implements INotifyCollectionChanged
Error message
The list must implements INotifyCollectionChanged
What it means
NonGenericObservableCollectionWrapper's constructor requires the wrapped IList<T> to implement both INotifyPropertyChanged and INotifyCollectionChanged, because it forwards those events to WPF bindings. If the list lacks INotifyCollectionChanged, it throws this ArgumentException immediately so silent binding breakage is avoided. Pass an ObservableList<T> (which implements both) or another fully observable collection.
Solutions
- Pass a Stride ObservableList<T> (or ObservableCollection<T>) as the wrapped list; it implements both required interfaces.
- If the source must stay a plain List<T>, replace it with ObservableList<T> and copy elements, or wrap changes manually.
- If you own the custom collection, implement INotifyCollectionChanged (raise CollectionChanged on Add/Remove/Clear/Replace).
Example fix
// before var wrapper = new MyWrapper(new List<Item>(items)); // after var list = new ObservableList<Item>(items); var wrapper = new MyWrapper(list);
Defensive patterns
Strategy: validation
Validate before calling
if (list == null) throw new ArgumentNullException(nameof(list));
if (!(list is INotifyPropertyChanged)) throw new ArgumentException("List must implement INotifyPropertyChanged", nameof(list));
if (!(list is INotifyCollectionChanged)) throw new ArgumentException("List must implement INotifyCollectionChanged", nameof(list)); Type guard
bool IsWrapperCompatible<T>(IList<T> list) => list is INotifyPropertyChanged && list is INotifyCollectionChanged;
Try / catch
try { var wrapper = new MyWrapper(list); }
catch (ArgumentException ex) { log.Error("Wrapped list is not fully observable", ex); list = new ObservableList<T>(list); wrapper = new MyWrapper(list); } Prevention
- Always wrap ObservableList<T> or ObservableCollection<T>, never List<T>.
- Add an interface assertion/unit test for any custom collection passed to the wrapper.
- Keep the backing collection type fixed when refactoring wrapper consumers.
When it happens
Trigger: Calling the protected NonGenericObservableCollectionWrapper(IList<T> list) constructor (from a derived wrapper class) with a List<T>, Collection<T>, array, or any IList<T> that implements INotifyPropertyChanged but not INotifyCollectionChanged.
Common situations: Subclassing the wrapper to expose a plain List<T> or a custom collection to WPF; swapping the backing collection type during a refactor from ObservableList<T> to a standard collection; third-party collection types that support change notification partially.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- ArgumentNullException: value
- The parameter of this command must be an instance of 'Window
- The target version is lower or equal to the start version.
- The upgrader has a target version higher that the current ve
- The relativePath argument is null or empty
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/9f9abacf8e62d798.
Report an issue: GitHub.
Appendix: source
Thrown at sources/presentation/Stride.Core.Presentation.Wpf/Collections/NonGenericObservableCollectionWrapper.cs:34
/// <remarks>
/// In some scenarii, <see cref="IList"/> does not support range changes on the collection (Especially when bound to a ListCollectionView).
/// This is why the <see cref="ObservableList{T}"/> and the <see cref="ObservableSet{T}"/> class does not implement this interface directly.
/// However this wrapper class can be used when the <see cref="IList"/> interface is required.
/// </remarks>
/// <typeparam name="T">The type of item contained in the <see cref="ObservableList{T}"/>.</typeparam>
public abstract class NonGenericObservableCollectionWrapper<T> : IList, IList<T>, INotifyPropertyChanged, INotifyCollectionChanged
{
[NotNull] protected readonly IList<T> List;
/// <summary>
/// Initializes a new instance of the <see cref="NonGenericObservableListWrapper{T}"/> class.
/// </summary>
/// <param name="list">The <see cref="ObservableList{T}"/> to wrap.</param>
protected NonGenericObservableCollectionWrapper([NotNull] IList<T> list)
{
if (list == null) throw new ArgumentNullException(nameof(list));
if (!(list is INotifyPropertyChanged)) throw new ArgumentException(@"The list must implements INotifyPropertyChanged", nameof(list));
if (!(list is INotifyCollectionChanged)) throw new ArgumentException(@"The list must implements INotifyCollectionChanged", nameof(list));
List = list;
((INotifyPropertyChanged)List).PropertyChanged += (sender, e) => PropertyChanged?.Invoke(this, e);
((INotifyCollectionChanged)List).CollectionChanged += (sender, e) => CollectionChanged?.Invoke(this, e);
}
/// <inheritdoc/>
public object this[int index] { get { return List[index]; } set { List[index] = (T)value; } }
/// <inheritdoc/>
T IList<T>.this[int index] { get { return List[index]; } set { List[index] = value; } }
/// <inheritdoc/>
public bool IsReadOnly => List.IsReadOnly;
/// <inheritdoc/>
public bool IsFixedSize => false;
View on GitHub (pinned to 96fad776d2)