{"record":{"id":"b87c63983664cbc7","repo":"dotnet/wpf","slug":"sr-undomanageralreadyattached","errorCode":null,"errorMessage":"SR.UndoManagerAlreadyAttached","messagePattern":"SR\\.UndoManagerAlreadyAttached","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/UndoManager.cs","lineNumber":100,"sourceCode":"        #region Internal Methods\n\n        /// <summary>\n        /// Defines a given FrameworkElement as a scope for undo service.\n        /// New instance of UndoManager created and attached to this element.\n        /// </summary>\n        /// <param name=\"scope\">\n        /// FrameworkElement to which new instance of UndoManager is attached.\n        /// </param>\n        /// <param name=\"undoManager\">\n        /// </param>\n        internal static void AttachUndoManager(DependencyObject scope, UndoManager undoManager)\n        {\n            ArgumentNullException.ThrowIfNull(scope);\n            ArgumentNullException.ThrowIfNull(undoManager);\n\n            if (undoManager is not null && ((UndoManager)undoManager)._scope != null)\n            {\n                throw new InvalidOperationException(SR.UndoManagerAlreadyAttached);\n            }\n\n            // Detach existing instance of undo manager if any\n            DetachUndoManager(scope);\n\n            // Attach the service to the scope via private dependency property\n            scope.SetValue(UndoManager.UndoManagerInstanceProperty, undoManager);\n            if (undoManager is not null)\n            {\n                Debug.Assert(((UndoManager)undoManager)._scope == null);\n                ((UndoManager)undoManager)._scope = scope;\n            }\n\n            undoManager.IsEnabled = true;\n        }\n\n        /// <summary>\n        /// Detaches an undo service from the given FrameworkElement.","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/UndoManager.cs#L82-L118","documentation":"UndoManager.AttachUndoManager throws InvalidOperationException(SR.UndoManagerAlreadyAttached) when the supplied UndoManager instance is already bound to another scope (its _scope is non-null). Each UndoManager can serve exactly one PropertyEditor/IScope at a time; attaching the same instance to a second scope would corrupt undo-unit bookkeeping, so WPF rejects it. Detach first or create a new UndoManager.","triggerScenarios":"Calling UndoManager.AttachUndoManager(scope, undoManager) where undoManager was previously attached to a different (or the same) scope and never detached — e.g. re-using one UndoManager across two TextBox scopes or re-attaching after a failed teardown.","commonSituations":"Re-using a cached UndoManager instance when recreating controls; attaching undo managers in a loop to multiple property scopes; framework code paths (e.g. editing services) that assume a fresh UndoManager but receive a recycled one.","solutions":["Call UndoManager.DetachUndoManager(oldScope) (or Detach on the manager) before attaching it to a new scope.","Create a new UndoManager instance for each scope instead of sharing one.","Check ((UndoManager)undoManager)._scope != null (or expose/track attachment state) before attaching and route accordingly.","Ensure error paths that abort attach logic still detach the manager to avoid leaked scope bindings."],"exampleFix":"// before\nUndoManager.AttachUndoManager(newScope, sharedUndoManager); // throws if already attached\n// after\nvar existingScope = ((UndoManager)sharedUndoManager).Scope;\nif (existingScope != null)\n{\n    UndoManager.DetachUndoManager(existingScope);\n}\nUndoManager.AttachUndoManager(newScope, sharedUndoManager);","handlingStrategy":"validation","validationCode":"static bool CanAttach(object undoManager) => undoManager is UndoManager um && um._scope == null; // or track attachment externally","typeGuard":"static bool IsAttachable(UndoManager um) => um != null && um.GetType().GetProperty(\"Scope\", BindingFlags.NonPublic | BindingFlags.Instance) == null; // prefer wrapping with your own attached-state flag","tryCatchPattern":"try { UndoManager.AttachUndoManager(scope, um); }\ncatch (InvalidOperationException) { UndoManager.DetachUndoManager(previousScope); UndoManager.AttachUndoManager(scope, um); }","preventionTips":["Never share one UndoManager across multiple scopes; allocate per scope","Always detach in teardown/cleanup paths symmetric to attach","Track ownership of UndoManager instances in your control infrastructure","Catch InvalidOperationException and detach-then-retry when recycling is intentional"],"tags":["wpf","undo","state"],"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"}