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

  1. Defer content changes until after layout: Dispatcher.BeginInvoke(DispatcherPriority.Loaded, ...) or Dispatcher.InvokeAsync.
  2. Move modifications to events outside the layout pass (e.g. after the measure/arrange completes, or on Loaded via BeginInvoke).
  3. 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

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


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)