dotnet/wpf · error · NotSupportedException

SR.Format(SR.UnexpectedCollectionChangeAction, args.Action)

Error message

SR.Format(SR.UnexpectedCollectionChangeAction, args.Action)

What it means

DocumentSequence._OnCollectionChanged reacts to changes in the sequence's child document collection. When the collection change action is something other than Add, Remove, or Replace, the handler has no logic to update paginators/child blocks and throws NotSupportedException with UnexpectedCollectionChangeAction. It guards an internal expectation that only supported INotifyCollectionChanged actions reach the sequence.

Solutions

  1. Only modify DocumentSequence.References through supported Add/Remove/Replace operations
  2. Avoid bulk mutations that fire NotifyCollectionChangedAction.Reset; rebuild the collection instead of resetting it
  3. Wrap References manipulation in try/catch NotSupportedException and fall back to recreating the DocumentSequence
  4. If you own the collection class, ensure it never raises Move/Reset; raise discrete Add/Remove events instead

Example fix

// before
references.Clear(); // fires Reset -> NotSupportedException
// after
foreach (var item in references.ToList()) { references.Remove(item); }
Defensive patterns

Strategy: try-catch

Validate before calling

if (args.Action != NotifyCollectionChangedAction.Add && args.Action != NotifyCollectionChangedAction.Remove && args.Action != NotifyCollectionChangedAction.Replace)
    throw new NotSupportedException($"Action {args.Action} is not supported by DocumentSequence.References");

Type guard

bool IsSupportedAction(NotifyCollectionChangedAction a) => a is NotifyCollectionChangedAction.Add or NotifyCollectionChangedAction.Remove or NotifyCollectionChangedAction.Replace;

Try / catch

try { references.Add(docRef); }
catch (NotSupportedException ex) { /* recreate DocumentSequence or log */ }

Prevention

When it happens

Trigger: Calling an API that raises CollectionChanged on DocumentSequence.References (or the underlying collection) with NotifyCollectionChangedAction.Reset or Move; any code mutating the References collection with a range action or custom action value.

Common situations: Custom collection implementations raising Reset after bulk edits; third-party code replacing the References collection behavior; WPF internal Reset notifications triggered when a FixedDocumentSequence reloads content.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/DocumentSequence.cs:747

                    if (paginator == null)
                    {
                        throw new ApplicationException(SR.DocumentReferenceHasInvalidDocument);
                    }

                    int addedPages = paginator.PageCount;
                    int firstPage = pageCount - addedPages;

                    if (addedPages > 0)
                    {
                        DocumentsTrace.FixedDocumentSequence.Content.Trace($"_OnCollectionChange: Add with IDP {paginator.GetHashCode()}");
                        _paginator.NotifyPaginationProgress(new PaginationProgressEventArgs(firstPage, addedPages));
                        _paginator.NotifyPagesChanged(new PagesChangedEventArgs(firstPage, addedPages));
                    }
                }
            }
            else
            {
                throw new NotSupportedException(SR.Format(SR.UnexpectedCollectionChangeAction, args.Action));
            }
        }


        // Take a child paginator and a page nubmer, find which global page number it corresponds to
        private bool _SynthesizeGlobalPageNumber(DynamicDocumentPaginator childPaginator, int childPageNumber, out int pageNumber)
        {
            pageNumber = 0;
            foreach (DocumentReference docRef in References)
            {
                DynamicDocumentPaginator innerPaginator = GetPaginator(docRef);
                if (innerPaginator != null)
                {
                    if (innerPaginator == childPaginator)
                    {
                        pageNumber += childPageNumber;
                        return true;
                    }

View on GitHub (pinned to 81131a70a4)