{"record":{"id":"b9b2afd3f6787bfd","repo":"dotnet/wpf","slug":"sr-format-sr-accesscollectionaftershutdown-collection","errorCode":null,"errorMessage":"SR.Format(SR.AccessCollectionAfterShutDown, collection)","messagePattern":"SR\\.Format\\(SR\\.AccessCollectionAfterShutDown, collection\\)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/BindingOperations.cs","lineNumber":364,"sourceCode":"        ///         int index = 3;\n        ///         int result = 0;\n        ///         BindingOperations.AccessCollection(\n        ///             _collection,\n        ///             () => { result = _collection[index]; },\n        ///             false);             // read-access\n        ///     }\n        /// Note that the access method refers to local variables (index, result)\n        /// of MyMethod, as well as to an instance variable (_collection) of the\n        /// 'this' object.\n        /// </notes>\n        public static void AccessCollection(\n                            IEnumerable collection,\n                            Action accessMethod,\n                            bool writeAccess)\n        {\n            ViewManager vm = ViewManager.Current;\n            if (vm == null)\n                throw new InvalidOperationException(SR.Format(SR.AccessCollectionAfterShutDown, collection));\n\n            vm.AccessCollection(collection, accessMethod, writeAccess);\n        }\n\n        /// <summary>\n        /// Returns a list of all binding expressions that are:\n        ///     a) top-level (do not belong to a parent MultiBindingExpression or BindingGroup)\n        ///     b) source-updating (binding mode is TwoWay or OneWayToSource)\n        ///     c) currently dirty or invalid\n        /// and d) attached to a descendant of the given DependencyObject (if non-null).\n        /// These are the bindings that may need attention before executing a command.\n        /// </summary>\n        public static ReadOnlyCollection<BindingExpressionBase> GetSourceUpdatingBindings(DependencyObject root)\n        {\n            List<BindingExpressionBase> list = DataBindEngine.CurrentDataBindEngine.CommitManager.GetBindingsInScope(root);\n            return new ReadOnlyCollection<BindingExpressionBase>(list);\n        }\n","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/BindingOperations.cs#L346-L382","documentation":"BindingOperations.AccessCollection throws InvalidOperationException(SR.AccessCollectionAfterShutDown) when the WPF ViewManager (a dispatcher-bound singleton) is null, meaning the Dispatcher has already shut down. Any binding-collection access routed through BindingOperations after dispatcher shutdown cannot be honored, since the view manager that serializes access no longer exists.","triggerScenarios":"Calling BindingOperations.AccessCollection (or APIs like CollectionView refresh/cross-thread collection access routed through it) from a background thread or during application teardown after Dispatcher.InvokeShutdown/Exit, when ViewManager.Current returns null.","commonSituations":"Background worker threads still pushing collection updates while the app is closing; asynchronous continuations (await/Dispatcher.BeginInvoke callbacks) firing after window close/dispatcher shutdown; unit tests that dispose the dispatcher but then touch bound collections.","solutions":["Check dispatcher lifetime before accessing the collection: if (dispatcher.HasShutdownStarted || dispatcher.HasShutdownFinished) skip the update.","Stop background producers on shutdown (CancellationToken, dispatcher ShutdownStarted event) so no collection access happens after teardown.","Marshal pending work to run before shutdown completes, or detach collections from bindings before closing the app."],"exampleFix":"// before\nawait Task.Run(() => LoadData());\nBindingOperations.AccessCollection(collection, () => collection.Add(item), true); // may throw at shutdown\n\n// after\nif (!dispatcher.HasShutdownStarted && !dispatcher.HasShutdownFinished)\n{\n    BindingOperations.AccessCollection(collection, () => collection.Add(item), true);\n}","handlingStrategy":"validation","validationCode":"var d = collection is ICollection c\n    ? Dispatcher.CurrentDispatcher : Dispatcher.CurrentDispatcher;\nif (d.HasShutdownStarted || d.HasShutdownFinished)\n    return; // skip collection access after shutdown","typeGuard":"bool IsDispatcherAlive(Dispatcher d) => d != null && !d.HasShutdownStarted && !d.HasShutdownFinished;","tryCatchPattern":"try { BindingOperations.AccessCollection(col, work, true); }\ncatch (InvalidOperationException) { /* dispatcher shut down; abandon or log */ }","preventionTips":["Cancel background collection producers on Dispatcher ShutdownStarted","Check HasShutdownStarted/HasShutdownFinished before cross-thread collection access","Detach bound collections during application teardown"],"tags":["wpf","dispatcher","shutdown","threading","binding"],"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-21T21:30:21.729Z"}