dotnet/wpf · error · ObjectDisposedException
SR.TextLineHasBeenDisposed
Error message
SR.TextLineHasBeenDisposed
What it means
FullTextLine.Draw throws ObjectDisposedException (SR.TextLineHasBeenDisposed) when drawing a TextLine whose unmanaged resources have already been released. Disposal happens via Dispose() or when the formatter invalidates the line (e.g. ClearLayoutThrow / Dispose on the TextFormatter or re-formatting).
Solutions
- Draw the line inside the same scope in which it was formatted, before Dispose.
- Do not wrap the TextFormatter in a using block if lines drawn later must remain valid.
- Cache the rendered output (DrawingContext/DrawingVisual) instead of the live TextLine.
- Re-format the line with TextFormatter.FormatLine if it has been disposed.
Example fix
// before
TextLine line;
using (var tf = TextFormatter.Create())
{
line = tf.FormatLine(...);
}
line.Draw(dc, origin, inversion); // ObjectDisposedException
// after
using (var tf = TextFormatter.Create())
{
var line = tf.FormatLine(...);
line.Draw(dc, origin, inversion);
} Defensive patterns
Strategy: validation
Validate before calling
void SafeDraw(TextLine line, DrawingContext dc, Point o, InvertAxes a)
{ if (line == null) throw new ArgumentNullException(nameof(line)); line.Draw(dc, o, a); } // call before any Dispose scope closes Try / catch
try { line.Draw(dc, origin, inversion); }
catch (ObjectDisposedException)
{
line = formatter.FormatLine(source, ...); // re-create
line.Draw(dc, origin, inversion);
} Prevention
- Draw lines before the TextFormatter using-block ends.
- Don't store TextLine objects beyond the layout pass; cache rendered visuals instead.
- Never call Dispose on lines you still intend to draw.
- Keep one TextFormatter per owner and dispose only at shutdown.
When it happens
Trigger: Calling line.Draw(drawingContext, origin, inversion) after line.Dispose(), after TextFormatterImp was disposed (e.g. using-block around the formatter closed), or after caching a line across layout passes and the formatter disposed it.
Common situations: Drawing cached TextLine objects in a later render pass after the formatting scope closed; storing lines in a view-model and drawing them after Dispose was called on cleanup; using 'using' around TextFormatter then drawing lines afterwards.
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.TextBreakpointHasBeenDisposed
- SR.TextPenaltyModuleHasBeenDisposed
- Collection_CopyTo_NumberOfElementsExceedsArrayLength
- EncryptedPackageEnvelope object was disposed.
- FixedPageReader
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/142e123643096f01.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/TextFormatting/FullTextLine.cs:539
/// <summary>
/// Draw line
/// </summary>
/// <param name="drawingContext">drawing context</param>
/// <param name="origin">drawing origin</param>
/// <param name="inversion">indicate the inversion of the drawing surface</param>
public override void Draw(
DrawingContext drawingContext,
Point origin,
InvertAxes inversion
)
{
ArgumentNullException.ThrowIfNull(drawingContext);
if ((_statusFlags & StatusFlags.IsDisposed) != 0)
{
throw new ObjectDisposedException(SR.TextLineHasBeenDisposed);
}
MatrixTransform antiInversion = TextFormatterImp.CreateAntiInversionTransform(
inversion,
_metrics._formatter.IdealToReal(_paragraphWidth, PixelsPerDip),
_metrics._formatter.IdealToReal(_metrics._height, PixelsPerDip)
);
if (antiInversion == null)
{
DrawTextLine(drawingContext, origin, null);
}
else
{
// Apply anti-inversion transform to correct the visual
drawingContext.PushTransform(antiInversion);
try
{View on GitHub (pinned to 81131a70a4)