dotnet/wpf · error · InvalidOperationException

SR.Format(SR.ItemsPanelNotAPanel, templateHolder.RootType)

Error message

SR.Format(SR.ItemsPanelNotAPanel, templateHolder.RootType)

What it means

The root element of an ItemsPanelTemplate's visual tree must derive from System.Windows.Controls.Panel. During ProcessTemplateBeforeSeal, if the template content's root type is null or cannot be assigned to Panel, WPF throws InvalidOperationException naming the offending root type.

Solutions

  1. Make the root element of the ItemsPanelTemplate a Panel (StackPanel, VirtualizingStackPanel, WrapPanel, UniformGrid, Grid, etc.).
  2. If decoration is needed, move it out of the ItemsPanelTemplate into the ItemTemplate or container style.
  3. Fix malformed XAML so the template body parses to a concrete root type.

Example fix

// before
<ItemsPanelTemplate>
  <Border><StackPanel/></Border>
</ItemsPanelTemplate>
// after
<ItemsPanelTemplate>
  <StackPanel/>
</ItemsPanelTemplate>
Defensive patterns

Strategy: validation

Validate before calling

bool IsPanelRoot(ItemsPanelTemplate t) =>
    (t?.VisualTree?.GetType() ?? t?.Template?.RootType?.UnderlyingType)?.IsSubclassOf(typeof(System.Windows.Controls.Panel)) == true;

Type guard

bool IsPanel(Type t) => t != null && typeof(System.Windows.Controls.Panel).IsAssignableFrom(t);

Try / catch

try { itemsControl.ItemsPanel = (ItemsPanelTemplate)Resources["ItemsPanel"]; } catch (InvalidOperationException ex) when (ex.Message.Contains("panel")) { itemsControl.ItemsPanel = new ItemsPanelTemplate { VisualTree = new FrameworkElementFactory(typeof(StackPanel)) }; }

Prevention

When it happens

Trigger: Placing a non-Panel element (e.g. <Grid> is fine, but <Border>, <TextBlock>, <ContentPresenter>, or a Control) as the direct child of ItemsPanelTemplate; an empty or malformed template body leaving RootType null.

Common situations: Copy-pasting a DataTemplate's content (often a Border/TextBlock) into ItemsPanelTemplate; typos or wrong xmlns producing a null root type; wrapping the panel in a decorator.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ItemsPanelTemplate.cs:117

        //      a. root element is a Panel
        // 2. Set IsItemsHost = true
        //

        internal override void ProcessTemplateBeforeSeal()
        {
            FrameworkElementFactory root;

            if( HasContent )
            {
                // This is a Baml-style template

                // Validate the root type (it must be a Panel)

                TemplateContent templateHolder = Template as TemplateContent;
                System.Xaml.XamlType panelType = templateHolder.SchemaContext.GetXamlType(typeof(Panel));
                if (templateHolder.RootType == null || !templateHolder.RootType.CanAssignTo(panelType))
                {
                    throw new InvalidOperationException(SR.Format(SR.ItemsPanelNotAPanel, templateHolder.RootType));
                }
            }

            else if ((root = this.VisualTree) != null)
            {
                // This is a FEF-style template
                if (!typeof(Panel).IsAssignableFrom(root.Type))
                    throw new InvalidOperationException(SR.Format(SR.ItemsPanelNotAPanel, root.Type));

                root.SetValue(Panel.IsItemsHostProperty, true);
            }
        }


        #endregion Internal Methods

        #region Protected Methods

View on GitHub (pinned to 81131a70a4)