dotnet/wpf · error · InvalidOperationException
SR.IllegalTreeChangeDetected
Error message
SR.IllegalTreeChangeDetected
What it means
TextBlock.VerifyTreeIsUnlocked throws InvalidOperationException when a tree/content modification is attempted while the TextBlock is in read-only mode — i.e. during the measure, arrange, or render pass. Modifying the text tree during layout would invalidate the in-progress layout, so WPF blocks it explicitly.
Solutions
- Defer content changes until after layout: Dispatcher.BeginInvoke(DispatcherPriority.Loaded, ...) or Dispatcher.InvokeAsync.
- Move modifications to events outside the layout pass (e.g. after the measure/arrange completes, or on Loaded via BeginInvoke).
- Restructure logic so layout responds to content changes rather than causing them.
Example fix
// before
protected override Size MeasureOverride(Size c) { textBlock.Text = Compute(); return base.MeasureOverride(c); }
// after
protected override Size MeasureOverride(Size c)
{
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => textBlock.Text = Compute()));
return base.MeasureOverride(c);
} Defensive patterns
Strategy: try-catch
Try / catch
try { textBlock.Text = newText; }
catch (InvalidOperationException) { Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => textBlock.Text = newText)); } Prevention
- Never modify TextBlock content in Measure/Arrange/Render
- Defer updates via Dispatcher.BeginInvoke(DispatcherPriority.Loaded)
- Keep layout read-only; queue writes for after the pass
When it happens
Trigger: Changing TextBlock content (Text, Inlines, AddChild) from within MeasureOverride/ArrangeOverride/OnRender, a LayoutUpdated handler during a layout pass, or property change callbacks that fire synchronously during layout.
Common situations: Setting Text from inside a custom panel's layout code; updating Inlines in a Loaded/SizeChanged handler that runs mid-layout; data-binding updates triggered synchronously during measure/arrange.
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
- SR.ArrangeReentrancyInvalid
- SR.ArrangeReentrancyInvalid
- SR.FlowDocumentInvalidContnetChange
- SR.MeasureReentrancyInvalid
- SR.MeasureReentrancyInvalid
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/b6836a9aa0b3c389.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs:3340
{
goodWidth = width;
}
}
// now format at goodwidth, with no reflow
line.Format(dcp, goodWidth, paragraphProperties, textLineBreak, textRunCache, ellipsis);
}
}
// ------------------------------------------------------------------
// Aborts calculation by throwing exception if world has changed
// while in measure / arrange / render process.
// ------------------------------------------------------------------
private void VerifyTreeIsUnlocked()
{
if (CheckFlags(Flags.TreeInReadOnlyMode))
{
throw new InvalidOperationException(SR.IllegalTreeChangeDetected);
}
}
// ------------------------------------------------------------------
// Decides if Text property needs to be serialized.
// ------------------------------------------------------------------
/// <summary>
/// This method is used by TypeDescriptor to determine if this property should
/// be serialized.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool ShouldSerializeText()
{
// We have to allow Text property serialization (at least for simple content)
// to preserve data-binding expressions. If we totally disable serialization
// data binding expression will be lost.
bool shouldSerialize = false;
View on GitHub (pinned to 81131a70a4)