dotnet/wpf · error · InvalidOperationException

SR.ElementMustBeInPopup

Error message

SR.ElementMustBeInPopup

What it means

A ToolTip must be rooted inside a Popup. When the ToolTip's visual parent changes (OnVisualParentChanged) and Popup.IsRootedInPopup determines the tooltip is not properly hosted in a Popup, an InvalidOperationException is thrown. ToolTips rely on Popup infrastructure for positioning and display, so attaching one directly into the visual tree elsewhere is invalid.

Solutions

  1. Set the tooltip via element.ToolTip = toolTip instead of adding it as a child.
  2. Host the ToolTip in a Popup via Popup.Child if you need manual control.
  3. Remove code that re-parents the ToolTip; use ToolTipService attached properties for customization.

Example fix

// before
stackPanel.Children.Add(myToolTip);
// after
button.ToolTip = myToolTip;
Defensive patterns

Strategy: validation

Validate before calling

if (element.ToolTip is ToolTip tt)
{
    // do not add tt to a visual tree; assign via .ToolTip instead
}
element.ToolTip = myToolTip;

Type guard

static bool IsPopupRooted(ToolTip tt) => tt.TemplatedParent is Popup || tt.Parent is Popup || tt.TemplatedParent != null && tt.TemplatedParent is Popup;

Try / catch

try { panel.Children.Add(control); }
catch (InvalidOperationException ex) when (ex.Message.Contains("ToolTip")) { /* assign via control.ToolTip instead */ }

Prevention

When it happens

Trigger: Assigning a ToolTip instance as a direct child of a panel/control (e.g. somePanel.Children.Add(toolTip)), or otherwise re-parenting a ToolTip outside a Popup, which fires OnVisualParentChanged.

Common situations: Confusing ToolTip with a normal control and adding it to a layout; programmatically building tooltips and inserting them into the tree instead of setting the FrameworkElement.ToolTip property.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ToolTip.cs:455

        /// <summary>
        /// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
        /// </summary>
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new ToolTipAutomationPeer(this);
        }

        /// <summary>
        /// Called when this element's visual parent changes
        /// </summary>
        /// <param name="oldParent"></param>
        protected internal override void OnVisualParentChanged(DependencyObject oldParent)
        {
            base.OnVisualParentChanged(oldParent);

            if (!Popup.IsRootedInPopup(_parentPopup, this))
            {
                throw new InvalidOperationException(SR.Format(SR.ElementMustBeInPopup, "ToolTip"));
            }
        }

        internal override void OnAncestorChanged()
        {
            base.OnAncestorChanged();

            if (!Popup.IsRootedInPopup(_parentPopup, this))
            {
                throw new InvalidOperationException(SR.Format(SR.ElementMustBeInPopup, "ToolTip"));
            }
        }

        #endregion

        #region Private Methods

        protected override void OnContentChanged(object oldContent, object newContent)

View on GitHub (pinned to 81131a70a4)