stride3d/stride · error · InvalidOperationException

Height must not be infinite when virtualizing horizontally.

Error message

Height must not be infinite when virtualizing horizontally.

What it means

When VirtualizingTilePanel is oriented horizontally, MeasureOverride requires a finite available height to determine items per line. If availableSize.Height is PositiveInfinity the line layout for virtualization cannot be computed, so the panel throws InvalidOperationException rather than producing incorrect virtualization.

Solutions

  1. Constrain height: set explicit Height or use VerticalAlignment="Stretch" in a Grid.
  2. Replace the enclosing vertical StackPanel with a Grid/DockPanel that supplies a finite height.
  3. Scroll horizontally within a fixed-height viewport instead of letting height be unbounded.

Example fix

// before
<StackPanel>
  <controls:VirtualizingTilePanel Orientation="Horizontal"/>
</StackPanel>
// after
<Grid>
  <controls:VirtualizingTilePanel Orientation="Horizontal" VerticalAlignment="Stretch"/>
</Grid>
Defensive patterns

Strategy: validation

Validate before calling

// Ensure a finite height constraint for horizontal orientation:
tilePanel.Height = double.IsInfinity(desiredHeight) ? 400 : desiredHeight;
// or host in a Grid with VerticalAlignment=Stretch.

Type guard

bool HasFiniteHeightConstraint(FrameworkElement el) =>
    !double.IsPositiveInfinity(el.Height) || el.VerticalAlignment == VerticalAlignment.Stretch;

Try / catch

try { panelHost.UpdateLayout(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Height must not be infinite")) { tilePanel.Height = 400; tilePanel.UpdateLayout(); }

Prevention

When it happens

Trigger: A horizontally-oriented VirtualizingTilePanel measured with infinite height: inside a vertical StackPanel, a ScrollViewer with VerticalScrollBarVisibility=Auto, or an unconstrained Grid Auto row.

Common situations: Vertical StackPanel wrapper around a horizontal tile list; auto-sized rows/cells; forgetting to set Height or VerticalAlignment when embedding the panel.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/bd8cf535592ac66e. Report an issue: GitHub.

Appendix: source

Thrown at sources/presentation/Stride.Core.Presentation.Wpf/Controls/VirtualizingTilePanel.cs:169

            ItemCount = parentItemsControl.Items.Count;
            ItemsPerLine = ItemCount;
            lineCount = ItemsPerLine;

            Size desiredSize;
            if (Orientation == Orientation.Vertical)
            {
                if (double.IsPositiveInfinity(availableSize.Width))
                    throw new InvalidOperationException("Width must not be infinite when virtualizing vertically.");

                ItemsPerLine = (int)Math.Ceiling(availableSize.Width - MinimumItemSpacing) / (int)(ItemSlotSize.Width + MinimumItemSpacing);
                ItemsPerLine = Math.Max(1, Math.Min(ItemsPerLine, ItemCount));
                lineCount = ComputeLineCount(ItemCount);
                desiredSize = new Size(availableSize.Width, lineCount * ItemSlotSize.Height);
            }
            else
            {
                if (double.IsPositiveInfinity(availableSize.Height))
                    throw new InvalidOperationException("Height must not be infinite when virtualizing horizontally.");

                ItemsPerLine = (int)Math.Ceiling(availableSize.Height - MinimumItemSpacing) / (int)Math.Ceiling(ItemSlotSize.Height + MinimumItemSpacing);
                ItemsPerLine = Math.Max(1, Math.Min(ItemsPerLine, ItemCount));
                lineCount = ComputeLineCount(ItemCount);
                desiredSize = new Size(lineCount * ItemSlotSize.Width, availableSize.Height);
            }

            UpdateScrollInfo(availableSize);

            int firstVisibleItemIndex, lastVisibleItemIndex;
            GetVisibilityRange(availableSize, out firstVisibleItemIndex, out lastVisibleItemIndex);

            // Get the generator position of the first visible data item
            var startPos = generator.GeneratorPositionFromIndex(firstVisibleItemIndex);

            var childIndex = (startPos.Offset == 0) ? startPos.Index : startPos.Index + 1;

            using (generator.StartAt(startPos, GeneratorDirection.Forward, true))

View on GitHub (pinned to 96fad776d2)