{"record":{"id":"ebe953d4ea6bdb56","repo":"dotnet/wpf","slug":"sr-enumerator-collectionchanged-transformcollection","errorCode":null,"errorMessage":"SR.Enumerator_CollectionChanged","messagePattern":"SR\\.Enumerator_CollectionChanged","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/TransformCollection.cs","lineNumber":832,"sourceCode":"            {\n                _list.ReadPreamble();\n\n                if (_version == _list._version)\n                {\n                    if (_index > -2 && _index < _list._collection.Count - 1)\n                    {\n                        _current = _list._collection[++_index];\n                        return true;\n                    }\n                    else\n                    {\n                        _index = -2; // -2 indicates \"past the end\"\n                        return false;\n                    }\n                }\n                else\n                {\n                    throw new InvalidOperationException(SR.Enumerator_CollectionChanged);\n                }\n            }\n\n            /// <summary>\n            /// Sets the enumerator to its initial position, which is before the\n            /// first element in the collection.\n            /// </summary>\n            public void Reset()\n            {\n                _list.ReadPreamble();\n\n                if (_version == _list._version)\n                {\n                    _index = -1;\n                }\n                else\n                {\n                    throw new InvalidOperationException(SR.Enumerator_CollectionChanged);","sourceCodeStart":814,"sourceCodeEnd":850,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/TransformCollection.cs#L814-L850","documentation":"TransformCollection.Enumerator.MoveNext() throws this InvalidOperationException when the underlying collection's version changed after the enumerator was created. WPF Freezable collections are versioned; any add/remove invalidates active enumerators because the documented enumerator contract requires throwing instead of returning stale or skipped data.","triggerScenarios":"Calling foreach or GetEnumerator() on a TransformCollection, then adding, removing, inserting, or clearing items (e.g. Add/Remove/Insert/Clear) before MoveNext() advances past the end of the modified region; the version check (_version != _list._version) fails inside MoveNext.","commonSituations":"Modifying a TransformGroup's Children collection while iterating it in the same loop; a rendering/animation callback mutating the collection during UI-thread enumeration; copying items out with a for-each while another handler edits the collection.","solutions":["Collect items to remove/add first, then apply the mutations after the foreach loop completes.","Iterate over a snapshot instead: foreach (Transform t in collection.ToArray()) — use System.Linq.","Replace the whole loop with a for loop over indices going backwards when removing items.","Wrap the enumeration in try/catch (InvalidOperationException) if the mutation is unavoidable and stale-iteration is acceptable."],"exampleFix":"// before\nforeach (Transform t in transformCollection)\n{\n    if (t is TranslateTransform) transformCollection.Remove(t); // throws on next MoveNext\n}\n// after\nvar toRemove = transformCollection.OfType<TranslateTransform>().ToList();\nforeach (var t in toRemove)\n{\n    transformCollection.Remove(t);\n}","handlingStrategy":"try-catch","validationCode":"// Before mutating while iterating, snapshot:\nbool willMutate = true;\nvar snapshot = willMutate ? transformCollection.ToArray() : null;","typeGuard":"static bool IsEnumerationValid(TransformCollection c, TransformCollection.Enumerator e) => true; // no public version access; prefer snapshotting\nstatic bool HasItems(TransformCollection c) => c != null && c.Count > 0;","tryCatchPattern":"try\n{\n    foreach (Transform t in transformCollection) { /* ... */ }\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"changed\"))\n{\n    // collection was modified during enumeration; retry with snapshot\n    foreach (Transform t in transformCollection.ToArray()) { /* ... */ }\n}","preventionTips":["Never mutate a TransformCollection inside foreach over the same collection.","Use ToList()/ToArray() snapshots when mutation during iteration is possible.","Batch UI-thread mutations outside animation/rendering callbacks.","Iterate backwards with an index-based for loop when removing items."],"tags":["wpf","enumerator","collection-modified","invalidoperationexception"],"backgroundTag":"invalid-state-transition","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}