dotnet/wpf · error · ArgumentOutOfRangeException

SR.Visual_ArgumentOutOfRange

Error message

SR.Visual_ArgumentOutOfRange

What it means

AdornedElementPlaceholder.GetVisualChild throws ArgumentOutOfRangeException when there is no child visual (_child == null) or the requested index is not 0. The control exposes at most one visual child (its Content), so index 0 is the only valid index and only when a child has been set.

Solutions

  1. Check VisualChildrenCount before calling GetVisualChild and skip the placeholder when it is 0
  2. Only request index 0; the placeholder has at most one visual child
  3. Ensure the placeholder is used inside a ControlTemplate with content set before walking its tree

Example fix

// before
var child = VisualTreeHelper.GetChild(placeholder, i);
// after
if (i < VisualTreeHelper.GetChildrenCount(placeholder))
{
    var child = VisualTreeHelper.GetChild(placeholder, i);
}
Defensive patterns

Strategy: validation

Validate before calling

if (VisualTreeHelper.GetChildrenCount(placeholder) > index)
    var child = VisualTreeHelper.GetChild(placeholder, index);

Type guard

bool HasVisualChild(AdornedElementPlaceholder p) => VisualTreeHelper.GetChildrenCount(p) > 0;

Prevention

When it happens

Trigger: Calling GetVisualChild(0) when the placeholder's Child/Content is null, or calling GetVisualChild with any index other than 0 (e.g. iterating with VisualTreeHelper.GetChild past index 0, or before the template content is applied).

Common situations: Walking the visual tree with VisualTreeHelper.GetChild on a ControlTemplate that hasn't set the placeholder's content yet; using VisualChildrenCount/GetVisualChild in a custom layout loop with a hardcoded child count; hitting the placeholder before ControlTemplate application completes.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/AdornedElementPlaceholder.cs:142

        /// <summary>
        /// Gets the Visual children count.
        /// </summary>
        protected override int VisualChildrenCount
        {
            get
            {
                return (_child == null) ? 0 : 1;
            }
        }

        /// <summary>
        /// Gets the Visual child at the specified index.
        /// </summary>
        protected override Visual GetVisualChild(int index)
        {
            if (_child == null || index != 0)
            {
                throw new ArgumentOutOfRangeException(nameof(index), index, SR.Visual_ArgumentOutOfRange);
            }
            return _child;
        }        

        /// <summary>
        /// Returns enumerator to logical children.
        /// </summary>
        protected internal override IEnumerator LogicalChildren
        {
            get
            {
                // Could optimize this code by returning EmptyEnumerator.Instance if _child == null.
                return new SingleChildEnumerator(_child);
            }
        }

        /// <summary>
        ///     This virtual method in called when IsInitialized is set to true and it raises an Initialized event

View on GitHub (pinned to 81131a70a4)