dotnet/wpf · error

' ' must be the root element of a tree, but has a logical…

Error message

'{0}' must be the root element of a tree, but has a logical parent '{1}'.

What it means

Popup.CreateRootPopupInternal (used by ContextMenu, ToolTip, etc.) requires the popup's Child to be a root element: before attaching it, the child must have no existing logical parent. WPF throws InvalidOperationException because an element with a logical parent cannot be reparented into the popup's visual/logical tree.

Solutions

  1. Remove the child from its current logical parent first (e.g. remove it from the containing Panel or set the property holding it to null).
  2. Use a fresh instance for the popup instead of a shared element (e.g. create a new instance from the DataTemplate/Resource).
  3. If intentionally moving the element, disconnect it via LogicalTreeHelper / clear the parent property before assigning Child.

Example fix

// before
var panel = (StackPanel)FindResource("myPanel");
popup.Child = panel; // panel already parented
// after
popup.Child = new StackPanel { Children = { new TextBlock { Text = "hi" } } }; // fresh instance
Defensive patterns

Strategy: validation

Validate before calling

if (LogicalTreeHelper.GetParent(child) != null || VisualTreeHelper.GetParent(child) != null)
    throw new InvalidOperationException("Child must be a root element before assigning to Popup.Child");

Type guard

bool CanBePopupChild(UIElement child) =>
    LogicalTreeHelper.GetParent(child) == null && VisualTreeHelper.GetParent(child) == null;

Try / catch

try { popup.Child = element; }
catch (InvalidOperationException ex) { /* detach element or instantiate a new one */ }

Prevention

When it happens

Trigger: Setting popup.Child (or passing a child to CreateRootPopup) with an element that already has a logical parent, e.g. an element that is still in a Window/Page's logical tree, or reusing a shared resource element across two popups.

Common situations: Reusing the same UIElement instance for two popups; moving a control from a window into a popup without calling Detach/RemoveChild first; declaring a resource element and assigning it both in XAML and code.

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

Appendix: source

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

        /// <summary>
        ///     Internal implementation of CreateRootPopup to allow tooltips to
        ///     override the popup's placement in case the tooltip comes from keyboard focus.
        /// </summary>
        /// <param name="popup">The parent popup that the child will be hooked up to.</param>
        /// <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);

View on GitHub (pinned to 81131a70a4)