dotnet/wpf · error · ArgumentException

SR.CannotBothBeNull (formatted with 'added', 'removed')

Error message

SR.CannotBothBeNull (formatted with 'added', 'removed')

What it means

The StrokeCollectionChangedEventArgs constructor requires at least one of the added/removed collections to be non-null; both being null leaves the event without any meaningful payload. This ArgumentException is thrown when constructing the event args with two nulls.

Solutions

  1. Pass at least one non-null StrokeCollection (use an empty StrokeCollection to indicate no changes)
  2. Guard before constructing: if both sources are null, skip raising the event

Example fix

// before
new StrokeCollectionChangedEventArgs(null, null);
// after
new StrokeCollectionChangedEventArgs(added ?? new StrokeCollection(), removed ?? new StrokeCollection());
Defensive patterns

Strategy: validation

Validate before calling

if (added == null && removed == null) return; // skip raising event

Type guard

static bool IsValidStrokeChangeArgs(StrokeCollection a, StrokeCollection r) => a != null || r != null;

Try / catch

try { var e = new StrokeCollectionChangedEventArgs(added, removed); OnChanged(e); } catch (ArgumentException) { /* both null: no change to report */ }

Prevention

When it happens

Trigger: Subclassing or raising a StrokeCollectionChanged event manually with new StrokeCollectionChangedEventArgs(null, null).

Common situations: Custom ink hosts forwarding stroke changes; test harnesses constructing event args with uninitialized collections.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/505175b2f1ff986f. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Ink/Events.cs:37

    public class StrokeCollectionChangedEventArgs : EventArgs
    {
        private StrokeCollection.ReadOnlyStrokeCollection _added;
        private StrokeCollection.ReadOnlyStrokeCollection _removed;
        private int _index = -1;

        /// <summary>Constructor</summary>
        internal StrokeCollectionChangedEventArgs(StrokeCollection added, StrokeCollection removed, int index) :
            this(added, removed)
        {
            _index = index;
        }

        /// <summary>Constructor</summary>
        public StrokeCollectionChangedEventArgs(StrokeCollection added, StrokeCollection removed)
        {
            if ( added == null && removed == null )
            {
                throw new ArgumentException(SR.Format(SR.CannotBothBeNull, "added", "removed"));
            }
            _added = ( added == null ) ? null : new StrokeCollection.ReadOnlyStrokeCollection(added);
            _removed = ( removed == null ) ? null : new StrokeCollection.ReadOnlyStrokeCollection(removed);
        }

        /// <summary>Set of strokes that where added, result may be an empty collection</summary>
        public StrokeCollection Added
        {
            get
            {
                if ( _added == null )
                {
                    _added = new StrokeCollection.ReadOnlyStrokeCollection(new StrokeCollection());
                }
                return _added;
            }
        }

View on GitHub (pinned to 81131a70a4)