{"record":{"id":"d56be563dcbe3aca","repo":"dotnet/wpf","slug":"sr-richtextbox-cantsetdocumentinsidechangeblock","errorCode":null,"errorMessage":"SR.RichTextBox_CantSetDocumentInsideChangeBlock","messagePattern":"SR\\.RichTextBox_CantSetDocumentInsideChangeBlock","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/RichTextBox.cs","lineNumber":365,"sourceCode":"            {\n                Invariant.Assert(_document != null);\n                return _document;\n            }\n\n            set\n            {\n                ArgumentNullException.ThrowIfNull(value);\n\n                if (value != _document &&\n                    value.StructuralCache != null && value.StructuralCache.TextContainer != null && \n                    value.StructuralCache.TextContainer.TextSelection != null)\n                {\n                    throw new ArgumentException(SR.RichTextBox_DocumentBelongsToAnotherRichTextBoxAlready);\n                }\n\n                if (_document != null && this.TextSelectionInternal.ChangeBlockLevel > 0)\n                {\n                    throw new InvalidOperationException(SR.RichTextBox_CantSetDocumentInsideChangeBlock);\n                }\n\n                if (value == _document)\n                {\n                    // Same document nothing to do.\n                    return;\n                }\n\n                // Identify the case for the _document initialization\n                bool initialSetting = _document == null;\n                \n                // Detach existing FlowDocument\n                if (_document != null)\n                {\n                    // Detach PageSize change listener\n                    _document.PageSizeChanged -= new EventHandler(this.OnPageSizeChangedHandler);\n\n                    // Remove the document from the logical tree","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/RichTextBox.cs#L347-L383","documentation":"While an editing change block is open (e.g. inside TextEditor.ChangeBlock, used by undo units and programmatic edits), RichTextBox.Document cannot be replaced. The setter throws InvalidOperationException(SR.RichTextBox_CantSetDocumentInsideChangeBlock) because swapping the text container mid-change would corrupt the undo stack and active selection.","triggerScenarios":"Setting Document from within an open change block: inside BeginChange/EndChange scopes, editing-event handlers such as TextChanged or selection events fired during a change block, or re-entrancy from a TextChanged handler that resets the Document.","commonSituations":"Reacting to TextChanged by replacing the Document; undo/redo callbacks that swap documents; custom commands wrapping edits in BeginChange and then assigning Document.","solutions":["Defer the assignment out of the change block, e.g. Dispatcher.BeginInvoke to run after editing completes.","Close the change block (EndChange/Dispose) before assigning Document.","Restructure so Document replacement is not triggered by editing events of the same control."],"exampleFix":"// before\nvoid OnTextChanged(...) { richTextBox.Document = newDoc; }\n// after\nvoid OnTextChanged(...) { Dispatcher.BeginInvoke(() => richTextBox.Document = newDoc); }","handlingStrategy":"try-catch","validationCode":"if (richTextBox.TextSelectionInternal?.ChangeBlockLevel > 0)\n    Dispatcher.BeginInvoke(() => richTextBox.Document = newDoc);\nelse\n    richTextBox.Document = newDoc;","typeGuard":"bool OutsideChangeBlock(RichTextBox rtb) => rtb.TextSelectionInternal == null || rtb.TextSelectionInternal.ChangeBlockLevel == 0;","tryCatchPattern":"try { richTextBox.Document = newDoc; }\ncatch (InvalidOperationException) { Dispatcher.BeginInvoke(() => richTextBox.Document = newDoc); }","preventionTips":["Never mutate Document inside TextChanged/selection handlers of the same control.","Ensure BeginChange scopes are closed (using/dispose) before swapping documents.","Marshal document swaps to the dispatcher queue when they originate from editing events."],"tags":["wpf","richtextbox","invalid-operation"],"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"}