Virtual Scrolling
Virtual scrolling provides an alternative to paging. Instead of utilizing a pager, the user scrolls vertically through all records in the data source.
To enhance rendering performance, the Grid reuses the same set of HTML elements. As the next data loads, a loading indicator appears on the cells. If the user scrolls back up after scrolling down to the next set of rows, the previous data reloads from the data source, similar to regular paging, with the scroll distance determining the data to be loaded.
You can also use the Blazor Grid virtualization for the Grid columns. See the Column Virtualization article for more information.
Using Virtual Scrolling
For the Blazor Grid virtualization to work, you need to:
- Set the
ScrollMode
parameter toGridScrollMode.Virtual
(the default value isScrollable
). -
Set the
Height
parameter. -
Set the
RowHeight
parameter. -
Set the
PageSize
parameter.
Setting a Value for the Height Parameter
Set the Height
parameter to a string
value. The value can be in:
- Pixels—for example,
Height="480px"
. - Percent—for example,
Height="30%"
. If you set theHeight
parameter to a percentage value, ensure that the wrapper of the Grid has a fixed height set in pixels. - Relative CSS units like vh, vmin, and vmax—for example,
Height="30vh"
.
The tabs below show how to set the Height
parameter with the different value options.
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<TelerikGrid OnRead="@OnGridRead"
TItem="@Product"
ScrollMode="@GridScrollMode.Virtual"
Height="480px" RowHeight="60" PageSize="20"
Sortable="true" FilterMode="@GridFilterMode.FilterRow">
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" Title="Product Name" />
<GridColumn Field="@nameof(Product.Stock)" />
</GridColumns>
</TelerikGrid>
@code {
private List<Product> GridData { get; set; } = new List<Product>();
private async Task OnGridRead(GridReadEventArgs args)
{
await Task.Delay(200); // simulate network delay
DataSourceResult result = GridData.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = result.Total;
args.AggregateResults = result.AggregateResults;
}
protected override void OnInitialized()
{
GridData = new List<Product>();
var rnd = new Random();
for (int i = 1; i <= 1000; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
Stock = rnd.Next(0, 100)
});
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public int Stock { get; set; }
}
}
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<div style="height: 600px">
<TelerikGrid OnRead="@OnGridRead"
TItem="@Product"
ScrollMode="@GridScrollMode.Virtual"
Height="100%" RowHeight="60" PageSize="20"
Sortable="true" FilterMode="@GridFilterMode.FilterRow">
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" Title="Product Name" />
<GridColumn Field="@nameof(Product.Stock)" />
</GridColumns>
</TelerikGrid>
</div>
@code {
private List<Product> GridData { get; set; } = new List<Product>();
private async Task OnGridRead(GridReadEventArgs args)
{
await Task.Delay(200); // simulate network delay
DataSourceResult result = GridData.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = result.Total;
args.AggregateResults = result.AggregateResults;
}
protected override void OnInitialized()
{
GridData = new List<Product>();
var rnd = new Random();
for (int i = 1; i <= 1000; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
Stock = rnd.Next(0, 100)
});
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public int Stock { get; set; }
}
}
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<div>Select a relative CSS unit from the RadioGroup to see how the Grid's Height reacts to different relative CSS units.</div>
<TelerikRadioGroup Data="@RelativeUnitsOptions"
Value="@ChosenRelativeUnit"
ValueChanged="@((int relativeUnitId) => RadioGroupChanged(relativeUnitId))"
ValueField="@nameof(RelativeUnitsDescriptor.RelativeUnitId)"
TextField="@nameof(RelativeUnitsDescriptor.RelativeUnitText)">
</TelerikRadioGroup>
<TelerikGrid OnRead="@OnGridRead"
TItem="@Product"
ScrollMode="@GridScrollMode.Virtual"
Height="@GridHeight" RowHeight="60" PageSize="20"
Sortable="true" FilterMode="@GridFilterMode.FilterRow">
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" Title="Product Name" />
<GridColumn Field="@nameof(Product.Stock)" />
</GridColumns>
</TelerikGrid>
@code {
private int ChosenRelativeUnit { get; set; }
private string GridHeight { get; set; }
private void RadioGroupChanged(int relativeUnitId)
{
ChosenRelativeUnit = relativeUnitId;
RelativeUnitsDescriptor relativeUnit = RelativeUnitsOptions.FirstOrDefault(x => x.RelativeUnitId == relativeUnitId);
GridHeight = "50" + relativeUnit.RelativeUnitText;
}
private async Task OnGridRead(GridReadEventArgs args)
{
await Task.Delay(200); // simulate network delay
DataSourceResult result = GridData.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = result.Total;
args.AggregateResults = result.AggregateResults;
}
protected override void OnInitialized()
{
GridHeight = "50" + RelativeUnitsOptions.FirstOrDefault().RelativeUnitText;
GridData = new List<Product>();
var rnd = new Random();
for (int i = 1; i <= 1000; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
Stock = rnd.Next(0, 100)
});
}
}
private List<Product> GridData { get; set; } = new List<Product>();
private List<RelativeUnitsDescriptor> RelativeUnitsOptions { get; set; } = new List<RelativeUnitsDescriptor>
{
new RelativeUnitsDescriptor { RelativeUnitId = 1, RelativeUnitText = "vm" },
new RelativeUnitsDescriptor { RelativeUnitId = 2, RelativeUnitText = "vmax" },
new RelativeUnitsDescriptor { RelativeUnitId = 3, RelativeUnitText = "vmin" }
};
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public int Stock { get; set; }
}
public class RelativeUnitsDescriptor
{
public int RelativeUnitId { get; set; }
public string RelativeUnitText { get; set; }
}
}
Setting a Value for the RowHeight Parameter
Set the RowHeight
parameter to a decimal
value which will always be interpreted as pixels (px
). The value of the RowHeight
must be greater than the height of the cell (or row) that the browser would normally render.
Consider the following specifics when setting the row height value:
- The Grid renders padding in the cells by default. The loading skeletons have a line height in order to render. This results in some minimum row heights, which can vary depending on the theme and custom CSS styles on the page.
- Ensure the height of the
td
element matches theRowHeight
when using the Row Template. - Do not change the value of the
RowHeight
parameter at runtime.
@* Remove the default padding and margin from the cells and remove the default line height of the loading skeletons to reduce the row height. *@
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<style>
.small-row-height .k-placeholder-line {
display: none;
}
.small-row-height.k-grid td {
margin: 0;
padding: 0;
}
</style>
<TelerikGrid OnRead="@OnGridRead"
TItem="@Product"
ScrollMode="@GridScrollMode.Virtual"
Height="480px" RowHeight="30" PageSize="20"
Sortable="true" FilterMode="@GridFilterMode.FilterRow"
Class="small-row-height">
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" Title="Product Name" />
<GridColumn Field="@nameof(Product.Stock)" />
</GridColumns>
</TelerikGrid>
@code {
private List<Product> GridData { get; set; } = new List<Product>();
private async Task OnGridRead(GridReadEventArgs args)
{
await Task.Delay(200); // simulate network delay
DataSourceResult result = GridData.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = result.Total;
args.AggregateResults = result.AggregateResults;
}
protected override void OnInitialized()
{
GridData = new List<Product>();
var rnd = new Random();
for (int i = 1; i <= 1000; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
Stock = rnd.Next(0, 100)
});
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public int Stock { get; set; }
}
}
Setting a Value for the PageSize Parameter
Set the PageSize
parameter to an int
value. The PageSize
determines how many rows are rendered at any given time and how many items are requested from the data source when loading data on demand. For optimal performance, use a page size that fills the grid's data viewport without being excessively large.
Limitations
The Blazor Grid virtualization primarily enhances client-side rendering performance and improves the user experience. However, it comes with the trade-off that certain features of the Grid are incompatible with it. An alternative approach is to utilize regular paging combined with manual data source operations to achieve the desired data retrieval performance.
These are the known limitations of the virtual scrolling feature:
Hierarchy is not supported.
Grouping is not supported. Loading Group Data On Demand is supported, however.