{"record":{"id":"b6836a9aa0b3c389","repo":"dotnet/wpf","slug":"sr-illegaltreechangedetected","errorCode":null,"errorMessage":"SR.IllegalTreeChangeDetected","messagePattern":"SR\\.IllegalTreeChangeDetected","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs","lineNumber":3340,"sourceCode":"                    {\n                        goodWidth = width;\n                    }\n                }\n\n                // now format at goodwidth, with no reflow\n                line.Format(dcp, goodWidth, paragraphProperties, textLineBreak, textRunCache, ellipsis);\n            }\n        }\n\n        // ------------------------------------------------------------------\n        // Aborts calculation by throwing exception if world has changed\n        // while in measure / arrange / render process.\n        // ------------------------------------------------------------------\n        private void VerifyTreeIsUnlocked()\n        {\n            if (CheckFlags(Flags.TreeInReadOnlyMode))\n            {\n                throw new InvalidOperationException(SR.IllegalTreeChangeDetected);\n            }\n        }\n\n        // ------------------------------------------------------------------\n        // Decides if Text property needs to be serialized.\n        // ------------------------------------------------------------------\n        /// <summary>\n        /// This method is used by TypeDescriptor to determine if this property should\n        /// be serialized.\n        /// </summary>\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public bool ShouldSerializeText()\n        {\n            // We have to allow Text property serialization (at least for simple content)\n            // to preserve data-binding expressions. If we totally disable serialization\n            // data binding expression will be lost.\n            bool shouldSerialize = false;\n","sourceCodeStart":3322,"sourceCodeEnd":3358,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs#L3322-L3358","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nprotected override Size MeasureOverride(Size c) { textBlock.Text = Compute(); return base.MeasureOverride(c); }\n// after\nprotected override Size MeasureOverride(Size c)\n{\n    Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => textBlock.Text = Compute()));\n    return base.MeasureOverride(c);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { textBlock.Text = newText; }\ncatch (InvalidOperationException) { Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => textBlock.Text = newText)); }","preventionTips":["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"],"tags":["wpf","textblock","layout","reentrancy"],"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"}