Overview

The grid virtualization system enables efficient rendering of arbitrarily large datasets by only materializing rows that are visible in the viewport, plus a configurable overscan buffer:

  • IVirtualDataSource — Contract for providing row data on demand
  • GridVirtualDataSource — Built-in implementation with preloading and total-count tracking
  • GridRowVirtualizer — Manages the visible window and requests data from the source
  • GridColumnVirtualizer — Column-level virtualization for wide datasets

IVirtualDataSource

public interface IVirtualDataSource
{
    long TotalRowCount { get; }
    IEnumerable<VirtualRowData> GetRows(long startIndex, int count);
    void PreloadWindow(long startIndex, int visibleCount, int overscan);
    void SetTotalRowCount(long count);
    event EventHandler? TotalRowCountChanged;
}

// Built-in implementations:
var source = GridVirtualDataSource.FromList(myList);
var source = GridVirtualDataSource.FromDataTable(myTable);
var source = GridVirtualDataSource.FromAsync(fetchCallback);

GridRowVirtualizer

var virtualizer = new GridRowVirtualizer(grid);
virtualizer.DataSource = myVirtualSource;

// Internal window management:
// visibleWindow: first visible row + visible row count
// overscanWindow: visibleWindow + padding above/below
// rowCache: Dictionary<long, VirtualRowData> for current window

Preloading Strategy

  • Visible window — Rows currently visible in the grid viewport
  • Overscan buffer — Extra rows above and below the visible window (default: 2x visible count)
  • Preloading — DataSource.PreloadWindow() is called on scroll to hint future data needs
  • Cache invalidation — Cache is invalidated when scrolling beyond buffer or when TotalRowCount changes

Examples

// Virtual grid with 10 million rows
var source = new GridVirtualDataSource();
source.SetTotalRowCount(10_000_000);
grid.RowVirtualizer.DataSource = source;

// Custom async data source
public class DatabaseDataSource : IVirtualDataSource
{
    public long TotalRowCount => db.Count<Customer>();

    public IEnumerable<VirtualRowData> GetRows(
        long startIndex, int count)
    {
        return db.Customers.Skip((int)startIndex).Take(count)
            .Select(c => VirtualRowData.FromObject(c));
    }
}