dotnet/wpf · error · InvalidOperationException

SR.CreateRootPopup_ChildHasVisualParent

Error message

SR.CreateRootPopup_ChildHasVisualParent

What it means

Sibling check in Popup.CreateRootPopupInternal: after verifying there is no logical parent, it also verifies the child has no visual parent (VisualTreeHelper.GetParent). An element already in a visual tree cannot be adopted as a popup root, so WPF throws InvalidOperationException. This typically fires when the element was rendered somewhere else.

Solutions

  1. Remove the element from its current visual parent (e.g. panel.Children.Remove(el)) before assigning it as the popup Child.
  2. Clone or instantiate a new element instead of moving the rendered one.
  3. Check VisualTreeHelper.GetParent(child) == null in a debug assertion before assigning Child.

Example fix

// before
popup.Child = this.someGrid; // someGrid still in window's visual tree
// after
rootPanel.Children.Remove(this.someGrid);
popup.Child = this.someGrid;
Defensive patterns

Strategy: validation

Validate before calling

if (VisualTreeHelper.GetParent(child) != null)
    throw new InvalidOperationException("Element still has a visual parent");

Type guard

bool HasVisualParent(UIElement el) => VisualTreeHelper.GetParent(el) != null;

Try / catch

try { popup.Child = element; }
catch (InvalidOperationException) {
    (VisualTreeHelper.GetParent(element) as Panel)?.Children.Remove(element);
    popup.Child = element;
}

Prevention

When it happens

Trigger: Assigning an element that is live in another visual tree (e.g. rendered inside a Window, ControlTemplate, or another Popup) as the Child of a Popup, ToolTip, or ContextMenu via CreateRootPopup/Child setter.

Common situations: Moving a visible control into a popup at runtime; sharing one Border/Grid element between a window and a popup; reusing template parts.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/aed364b085949af9. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/Primitives/Popup.cs:895

        /// <param name="child">The element to be the child of the popup.</param>
        /// <param name="bindTreatMousePlacementAsBottomProperty">Whether to bind TreatMousePlacementAsBottomProperty to the child's FromKeyboard property</param>
        internal static void CreateRootPopupInternal(Popup popup, UIElement child, bool bindTreatMousePlacementAsBottomProperty)
        {
            ArgumentNullException.ThrowIfNull(popup);
            ArgumentNullException.ThrowIfNull(child);

            Debug.Assert(!bindTreatMousePlacementAsBottomProperty || child is ToolTip, "child must be a Tooltip to bind TreatMousePlacementAsBottomProperty");

            // When we get here, the Child must not have already been visually or logically parented.
            object currentParent = null;
            if ((currentParent = LogicalTreeHelper.GetParent(child)) != null)
            {
                throw new InvalidOperationException(SR.Format(SR.CreateRootPopup_ChildHasLogicalParent, child, currentParent));
            }

            if ((currentParent = VisualTreeHelper.GetParent(child)) != null)
            {
                throw new InvalidOperationException(SR.Format(SR.CreateRootPopup_ChildHasVisualParent, child, currentParent));
            }

            // PlacementTarget must be set before hooking up the child so that resource
            // lookups can work.  The Popup for tooltip and context menu isn't in the tree
            // so FE relies on GetUIParentCore to return the placement target as the
            // effective logical parent
            Binding binding = new Binding("PlacementTarget")
            {
                Mode = BindingMode.OneWay,
                Source = child
            };
            popup.SetBinding(PlacementTargetProperty, binding);

            // NOTE: this will hook up child as a logical child of Popup.
            // If at a later date this is not desired, then modify the hookup to avoid the logical hookup.
            //
            // NOTE: Logical linking is necessary if property invalidations are to propagate down
            // the tree into the child (unless at a later date an alternate method has been created).

View on GitHub (pinned to 81131a70a4)