dotnet/wpf · error · InvalidOperationException

SR.RowCacheRecalcWithNoPageCache

Error message

SR.RowCacheRecalcWithNoPageCache

What it means

RowCache.RecalcLayoutForScaleOrSpacing recomputes row extents after zoom scale or page spacing changes. It requires an associated PageCache (PageCache property); when none is set, InvalidOperationException(SR.RowCacheRecalcWithNoPageCache) is thrown because extents cannot be computed without page sizes.

Solutions

  1. Set/assign the PageCache (document) before changing Scale or spacing properties.
  2. Guard property setters: skip recalculation when PageCache is null until the document is loaded.
  3. Defer zoom/spacing initialization to the document-loaded event rather than the control constructor.
  4. Verify the viewer's Document dependency property is bound before applying saved layout settings.

Example fix

// before
rowCache.Scale = savedZoom; // PageCache not yet assigned
// after
if (rowCache.PageCache != null)
{
    rowCache.Scale = savedZoom;
}
Defensive patterns

Strategy: validation

Validate before calling

if (rowCache.PageCache != null)
{
    rowCache.Scale = zoom;
}

Type guard

bool HasPageCache(RowCache rc) => rc?.PageCache != null;

Try / catch

try { rowCache.RecalcLayoutForScaleOrSpacing(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("PageCache"))
{
    // document not yet assigned: defer recalc until load completes
}

Prevention

When it happens

Trigger: Changing Scale, HorizontalPageSpacing, or VerticalPageSpacing on a RowCache that was constructed without (or before being given) a PageCache, causing RecalcLayoutForScaleOrSpacing to run with PageCache == null.

Common situations: Binding zoom/spacing properties on a MultiPage viewer before the document (and its PageCache) is assigned; constructing RowCache manually in custom viewer controls and tweaking layout properties during initialization.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/RowCache.cs:441

                    break;
                }

                //This row is visible, add it to the count.
                rowCount++;
            }
        }

        /// <summary>
        /// Recalculates the current row layout by applying the current Scale
        /// and PageSpacing to the current row layout.  It does not change the
        /// contents of the rows.
        /// </summary>
        public void RecalcLayoutForScaleOrSpacing()
        {
            //Throw execption if we have no PageCache
            if (PageCache == null)
            {
                throw new InvalidOperationException(SR.RowCacheRecalcWithNoPageCache);
            }

            //Reset the extents
            _extentWidth = 0.0;
            _extentHeight = 0.0;

            //Walk through each row and based on the pages on the row,
            //recalculate the width and height of the row.
            double currentOffset = 0.0;
            for (int i = 0; i < _rowCache.Count; i++)
            {
                //Get this row and save off the page count.
                RowInfo currentRow = _rowCache[i];
                int pageCount = currentRow.PageCount;

                //Clear the pages so we can add the new, rescaled ones.
                currentRow.ClearPages();
                currentRow.VerticalOffset = currentOffset;

View on GitHub (pinned to 81131a70a4)