AspNetCore.Docs/aspnetcore/blazor/components/virtualization.md

18 KiB

title author description monikerRange ms.author ms.custom ms.date no-loc uid
ASP.NET Core Blazor component virtualization guardrex Learn how to use component virtualization in ASP.NET Core Blazor apps. >= aspnetcore-5.0 riande mvc 02/26/2021
Home
Privacy
Kestrel
appsettings.json
ASP.NET Core Identity
cookie
Cookie
Blazor
Blazor Server
Blazor WebAssembly
Identity
Let's Encrypt
Razor
SignalR
blazor/components/virtualization

ASP.NET Core Blazor component virtualization

::: moniker range=">= aspnetcore-6.0"

Improve the perceived performance of component rendering using the Blazor framework's built-in virtualization support with the Virtualize component. Virtualization is a technique for limiting UI rendering to just the parts that are currently visible. For example, virtualization is helpful when the app must render a long list of items and only a subset of items is required to be visible at any given time.

Use the Virtualize component when:

  • Rendering a set of data items in a loop.
  • Most of the items aren't visible due to scrolling.
  • The rendered items are the same size.

When the user scrolls to an arbitrary point in the Virtualize component's list of items, the component calculates the visible items to show. Unseen items aren't rendered.

Without virtualization, a typical list might use a C# foreach loop to render each item in a list. In the following example:

  • allFlights is a collection of airplane flights.
  • The FlightSummary component displays details about each flight.
  • The @key directive attribute preserves the relationship of each FlightSummary component to its rendered flight by the flight's FlightId.
<div style="height:500px;overflow-y:scroll">
    @foreach (var flight in allFlights)
    {
        <FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
    }
</div>

If the collection contains thousands of flights, rendering the flights takes a long time and users experience a noticeable UI lag. Most of the flights aren't rendered because they fall outside of the height of the <div> element.

Instead of rendering the entire list of flights at once, replace the foreach loop in the preceding example with the Virtualize component:

<div style="height:500px;overflow-y:scroll">
    <Virtualize Items="@allFlights" Context="flight">
        <FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
    </Virtualize>
</div>

If a context isn't specified with the Context parameter, use the value of context in the item content template to access each flight's members:

<div style="height:500px;overflow-y:scroll">
    <Virtualize Items="@allFlights">
        <FlightSummary @key="context.FlightId" Details="@context.Summary" />
    </Virtualize>
</div>

The Virtualize component:

  • Calculates the number of items to render based on the height of the container and the size of the rendered items.
  • Recalculates and rerenders the items as the user scrolls.
  • Only fetches the slice of records from an external API that correspond to the current visible region, instead of downloading all of the data from the collection.

The item content for the Virtualize component can include:

  • Plain HTML and Razor code, as the preceding example shows.
  • One or more Razor components.
  • A mix of HTML/Razor and Razor components.

Item provider delegate

If you don't want to load all of the items into memory, you can specify an items provider delegate method to the component's xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.ItemsProvider%2A?displayProperty=nameWithType parameter that asynchronously retrieves the requested items on demand. In the following example, the LoadEmployees method provides the items to the Virtualize component:

<Virtualize Context="employee" ItemsProvider="@LoadEmployees">
    <p>
        @employee.FirstName @employee.LastName has the 
        job title of @employee.JobTitle.
    </p>
</Virtualize>

The items provider receives an xref:Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderRequest, which specifies the required number of items starting at a specific start index. The items provider then retrieves the requested items from a database or other service and returns them as an xref:Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult%601 along with a count of the total items. The items provider can choose to retrieve the items with each request or cache them so that they're readily available.

A Virtualize component can only accept one item source from its parameters, so don't attempt to simultaneously use an items provider and assign a collection to Items. If both are assigned, an xref:System.InvalidOperationException is thrown when the component's parameters are set at runtime.

The following LoadEmployees method example loads employees from an EmployeeService (not shown):

private async ValueTask<ItemsProviderResult<Employee>> LoadEmployees(
    ItemsProviderRequest request)
{
    var numEmployees = Math.Min(request.Count, totalEmployees - request.StartIndex);
    var employees = await EmployeesService.GetEmployeesAsync(request.StartIndex, 
        numEmployees, request.CancellationToken);

    return new ItemsProviderResult<Employee>(employees, totalEmployees);
}

xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.RefreshDataAsync%2A?displayProperty=nameWithType instructs the component to rerequest data from its xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.ItemsProvider%2A. This is useful when external data changes. There's no need to call xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.RefreshDataAsync%2A when using xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.Items%2A.

Placeholder

Because requesting items from a remote data source might take some time, you have the option to render a placeholder with item content:

<Virtualize Context="employee" ItemsProvider="@LoadEmployees">
    <ItemContent>
        <p>
            @employee.FirstName @employee.LastName has the 
            job title of @employee.JobTitle.
        </p>
    </ItemContent>
    <Placeholder>
        <p>
            Loading&hellip;
        </p>
    </Placeholder>
</Virtualize>

Item size

The height of each item in pixels can be set with xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.ItemSize%2A?displayProperty=nameWithType (default: 50). The following example changes the height of each item from the default of 50 pixels to 25 pixels:

<Virtualize Context="employee" Items="@employees" ItemSize="25">
    ...
</Virtualize>

By default, the Virtualize component measures the rendering size (height) of individual items after the initial render occurs. Use xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.ItemSize%2A to provide an exact item size in advance to assist with accurate initial render performance and to ensure the correct scroll position for page reloads. If the default xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.ItemSize%2A causes some items to render outside of the currently visible view, a second re-render is triggered. To correctly maintain the browser's scroll position in a virtualized list, the initial render must be correct. If not, users might view the wrong items.

Overscan count

xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.OverscanCount%2A?displayProperty=nameWithType determines how many additional items are rendered before and after the visible region. This setting helps to reduce the frequency of rendering during scrolling. However, higher values result in more elements rendered in the page (default: 3). The following example changes the overscan count from the default of three items to four items:

<Virtualize Context="employee" Items="@employees" OverscanCount="4">
    ...
</Virtualize>

State changes

When making changes to items rendered by the Virtualize component, call xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A to force re-evaluation and rerendering of the component. For more information, see xref:blazor/components/rendering.

::: moniker-end

::: moniker range=">= aspnetcore-5.0 < aspnetcore-6.0"

Improve the perceived performance of component rendering using the Blazor framework's built-in virtualization support with the Virtualize component. Virtualization is a technique for limiting UI rendering to just the parts that are currently visible. For example, virtualization is helpful when the app must render a long list of items and only a subset of items is required to be visible at any given time.

Use the Virtualize component when:

  • Rendering a set of data items in a loop.
  • Most of the items aren't visible due to scrolling.
  • The rendered items are the same size.

When the user scrolls to an arbitrary point in the Virtualize component's list of items, the component calculates the visible items to show. Unseen items aren't rendered.

Without virtualization, a typical list might use a C# foreach loop to render each item in a list. In the following example:

  • allFlights is a collection of airplane flights.
  • The FlightSummary component displays details about each flight.
  • The @key directive attribute preserves the relationship of each FlightSummary component to its rendered flight by the flight's FlightId.
<div style="height:500px;overflow-y:scroll">
    @foreach (var flight in allFlights)
    {
        <FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
    }
</div>

If the collection contains thousands of flights, rendering the flights takes a long time and users experience a noticeable UI lag. Most of the flights aren't rendered because they fall outside of the height of the <div> element.

Instead of rendering the entire list of flights at once, replace the foreach loop in the preceding example with the Virtualize component:

<div style="height:500px;overflow-y:scroll">
    <Virtualize Items="@allFlights" Context="flight">
        <FlightSummary @key="flight.FlightId" Details="@flight.Summary" />
    </Virtualize>
</div>

If a context isn't specified with the Context parameter, use the value of context in the item content template to access each flight's members:

<div style="height:500px;overflow-y:scroll">
    <Virtualize Items="@allFlights">
        <FlightSummary @key="context.FlightId" Details="@context.Summary" />
    </Virtualize>
</div>

The Virtualize component:

  • Calculates the number of items to render based on the height of the container and the size of the rendered items.
  • Recalculates and rerenders the items as the user scrolls.
  • Only fetches the slice of records from an external API that correspond to the current visible region, instead of downloading all of the data from the collection.

The item content for the Virtualize component can include:

  • Plain HTML and Razor code, as the preceding example shows.
  • One or more Razor components.
  • A mix of HTML/Razor and Razor components.

Item provider delegate

If you don't want to load all of the items into memory, you can specify an items provider delegate method to the component's xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.ItemsProvider%2A?displayProperty=nameWithType parameter that asynchronously retrieves the requested items on demand. In the following example, the LoadEmployees method provides the items to the Virtualize component:

<Virtualize Context="employee" ItemsProvider="@LoadEmployees">
    <p>
        @employee.FirstName @employee.LastName has the 
        job title of @employee.JobTitle.
    </p>
</Virtualize>

The items provider receives an xref:Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderRequest, which specifies the required number of items starting at a specific start index. The items provider then retrieves the requested items from a database or other service and returns them as an xref:Microsoft.AspNetCore.Components.Web.Virtualization.ItemsProviderResult%601 along with a count of the total items. The items provider can choose to retrieve the items with each request or cache them so that they're readily available.

A Virtualize component can only accept one item source from its parameters, so don't attempt to simultaneously use an items provider and assign a collection to Items. If both are assigned, an xref:System.InvalidOperationException is thrown when the component's parameters are set at runtime.

The following LoadEmployees method example loads employees from an EmployeeService (not shown):

private async ValueTask<ItemsProviderResult<Employee>> LoadEmployees(
    ItemsProviderRequest request)
{
    var numEmployees = Math.Min(request.Count, totalEmployees - request.StartIndex);
    var employees = await EmployeesService.GetEmployeesAsync(request.StartIndex, 
        numEmployees, request.CancellationToken);

    return new ItemsProviderResult<Employee>(employees, totalEmployees);
}

xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.RefreshDataAsync%2A?displayProperty=nameWithType instructs the component to rerequest data from its xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.ItemsProvider%2A. This is useful when external data changes. There's no need to call xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.RefreshDataAsync%2A when using xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.Items%2A.

Placeholder

Because requesting items from a remote data source might take some time, you have the option to render a placeholder with item content:

<Virtualize Context="employee" ItemsProvider="@LoadEmployees">
    <ItemContent>
        <p>
            @employee.FirstName @employee.LastName has the 
            job title of @employee.JobTitle.
        </p>
    </ItemContent>
    <Placeholder>
        <p>
            Loading&hellip;
        </p>
    </Placeholder>
</Virtualize>

Item size

The height of each item in pixels can be set with xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.ItemSize%2A?displayProperty=nameWithType (default: 50). The following example changes the height of each item from the default of 50 pixels to 25 pixels:

<Virtualize Context="employee" Items="@employees" ItemSize="25">
    ...
</Virtualize>

By default, the Virtualize component measures the rendering size (height) of individual items after the initial render occurs. Use xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.ItemSize%2A to provide an exact item size in advance to assist with accurate initial render performance and to ensure the correct scroll position for page reloads. If the default xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.ItemSize%2A causes some items to render outside of the currently visible view, a second re-render is triggered. To correctly maintain the browser's scroll position in a virtualized list, the initial render must be correct. If not, users might view the wrong items.

Overscan count

xref:Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize%601.OverscanCount%2A?displayProperty=nameWithType determines how many additional items are rendered before and after the visible region. This setting helps to reduce the frequency of rendering during scrolling. However, higher values result in more elements rendered in the page (default: 3). The following example changes the overscan count from the default of three items to four items:

<Virtualize Context="employee" Items="@employees" OverscanCount="4">
    ...
</Virtualize>

State changes

When making changes to items rendered by the Virtualize component, call xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A to force re-evaluation and rerendering of the component. For more information, see xref:blazor/components/rendering.

::: moniker-end