dotnet/wpf · error · InvalidOperationException
SR.RichTextBox_CantSetDocumentInsideChangeBlock
Error message
SR.RichTextBox_CantSetDocumentInsideChangeBlock
What it means
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.
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.
Example fix
// before
void OnTextChanged(...) { richTextBox.Document = newDoc; }
// after
void OnTextChanged(...) { Dispatcher.BeginInvoke(() => richTextBox.Document = newDoc); } Defensive patterns
Strategy: try-catch
Validate before calling
if (richTextBox.TextSelectionInternal?.ChangeBlockLevel > 0)
Dispatcher.BeginInvoke(() => richTextBox.Document = newDoc);
else
richTextBox.Document = newDoc; Type guard
bool OutsideChangeBlock(RichTextBox rtb) => rtb.TextSelectionInternal == null || rtb.TextSelectionInternal.ChangeBlockLevel == 0;
Try / catch
try { richTextBox.Document = newDoc; }
catch (InvalidOperationException) { Dispatcher.BeginInvoke(() => richTextBox.Document = newDoc); } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Reacting to TextChanged by replacing the Document; undo/redo callbacks that swap documents; custom commands wrapping edits in BeginChange and then assigning Document.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Animation_Invalid_DefaultValue
- Cannot remove signature from read-only file.
- Image_EncoderNoColorContext
- Image_EncoderNoGlobalMetadata
- Image_EncoderNoGlobalThumbnail
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/d56be563dcbe3aca.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/RichTextBox.cs:365
{
Invariant.Assert(_document != null);
return _document;
}
set
{
ArgumentNullException.ThrowIfNull(value);
if (value != _document &&
value.StructuralCache != null && value.StructuralCache.TextContainer != null &&
value.StructuralCache.TextContainer.TextSelection != null)
{
throw new ArgumentException(SR.RichTextBox_DocumentBelongsToAnotherRichTextBoxAlready);
}
if (_document != null && this.TextSelectionInternal.ChangeBlockLevel > 0)
{
throw new InvalidOperationException(SR.RichTextBox_CantSetDocumentInsideChangeBlock);
}
if (value == _document)
{
// Same document nothing to do.
return;
}
// Identify the case for the _document initialization
bool initialSetting = _document == null;
// Detach existing FlowDocument
if (_document != null)
{
// Detach PageSize change listener
_document.PageSizeChanged -= new EventHandler(this.OnPageSizeChangedHandler);
// Remove the document from the logical treeView on GitHub (pinned to 81131a70a4)