dotnet/wpf · error · InvalidOperationException

Cannot attach a Visual that is already attached.

Error message

Cannot attach a Visual that is already attached.

What it means

Renderer.AttachIncrementalRendering throws InvalidOperationException (SR.CannotAttachVisualTwice) when the same Visual is attached more than once. The Renderer keeps a list of attached visuals to prevent duplicate registration for incremental ink rendering.

Solutions

  1. Track attach state and only call AttachIncrementalRendering if not already attached
  2. Call DetachIncrementalRendering before re-attaching the visual
  3. Ensure each Renderer gets its own host visual
  4. Guard with a try-catch or a Contains check on your own attached-visual bookkeeping

Example fix

// before
renderer.AttachIncrementalRendering(hostVisual, constraints); // may run twice
// after
if (!attached.Contains(hostVisual))
{
    renderer.AttachIncrementalRendering(hostVisual, constraints);
    attached.Add(hostVisual);
}
Defensive patterns

Strategy: validation

Validate before calling

// Track attached visuals yourself
bool alreadyAttached = attachedVisuals.Contains(hostVisual);
if (!alreadyAttached) renderer.AttachIncrementalRendering(hostVisual, strokers);

Type guard

bool CanAttach(Renderer r, Visual v) => v != null && !attachedVisuals.Contains(v);

Try / catch

try { renderer.AttachIncrementalRendering(visual, strokers); }
catch (InvalidOperationException) { /* already attached - skip */ }

Prevention

When it happens

Trigger: Calling AttachIncrementalRendering twice with the same visual, or attaching a visual already attached through another InkPresenter/Renderer sharing it.

Common situations: Re-initializing an InkCanvas/dynamic renderer without detaching first, attaching the same host visual to multiple renderers, event handler re-entrancy causing double attach.

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/dc3cccd32826a556. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Ink/Renderer.cs:289

        /// <param name="drawingAttributes">drawing attributes that used in the incremental rendering</param>
        internal void AttachIncrementalRendering(Visual visual, DrawingAttributes drawingAttributes)
        {
            // Check the input parameters
            ArgumentNullException.ThrowIfNull(visual);
            ArgumentNullException.ThrowIfNull(drawingAttributes);

            //harden against eaten exceptions
            bool exceptionRaised = false;

            // Verify that the visual hasn't been attached already
            if (_attachedVisuals != null)
            {
                foreach(Visual alreadyAttachedVisual in _attachedVisuals)
                {
                    if (visual == alreadyAttachedVisual)
                    {
                        exceptionRaised = true;
                        throw new System.InvalidOperationException(SR.CannotAttachVisualTwice);
                    }
                }
            }
            else
            {
                // Create the list to register attached visuals in
                _attachedVisuals = new List<Visual>();
            }


            if (!exceptionRaised)
            {
                // The position of the visual in the tree depends on the drawingAttributes
                // Find the appropriate parent visual to attach this visual to.
                ContainerVisual parent = drawingAttributes.IsHighlighter ? GetContainerVisual(drawingAttributes) : _incrementalRenderingVisuals;

                // Attach the visual to the tree
                parent.Children.Add(visual);

View on GitHub (pinned to 81131a70a4)