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

36 KiB

title author description monikerRange ms.author ms.custom ms.date uid
ASP.NET Core Blazor QuickGrid component guardrex The QuickGrid component is a Razor component for quickly and efficiently displaying data in tabular form. >= aspnetcore-8.0 riande mvc 11/12/2024 blazor/components/quickgrid

ASP.NET Core Blazor QuickGrid component

[!INCLUDE]

The QuickGrid component is a Razor component for quickly and efficiently displaying data in tabular form. QuickGrid provides a simple and convenient data grid component for common grid rendering scenarios and serves as a reference architecture and performance baseline for building data grid components. QuickGrid is highly optimized and uses advanced techniques to achieve optimal rendering performance.

Package

Add a package reference for the Microsoft.AspNetCore.Components.QuickGrid package.

[!INCLUDE]

Sample app

For various QuickGrid demonstrations, see the QuickGrid for Blazor sample app. The demo site is hosted on GitHub Pages. The site loads fast thanks to static prerendering using the community-maintained BlazorWasmPrerendering.Build GitHub project.

QuickGrid implementation

To implement a QuickGrid component:

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

:::moniker-end

:::moniker range="< aspnetcore-9.0"

:::moniker-end

For example, add the following component to render a grid.

For Blazor Web Apps, the QuickGrid component must adopt an interactive render mode to enable interactive features, such as paging and sorting.

PromotionGrid.razor:

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

:::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/PromotionGrid.razor":::

:::moniker-end

:::moniker range="< aspnetcore-9.0"

:::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/PromotionGrid.razor":::

:::moniker-end

Access the component in a browser at the relative path /promotion-grid.

There aren't current plans to extend QuickGrid with features that full-blown commercial grids tend to offer, for example, hierarchical rows, drag-to-reorder columns, or Excel-like range selections. If you require advanced features that you don't wish to develop on your own, continue using third-party grids.

Sort by column

The QuickGrid component can sort items by columns. In Blazor Web Apps, sorting requires the component to adopt an interactive render mode.

Add Sortable="true" (xref:Microsoft.AspNetCore.Components.QuickGrid.ColumnBase%601.Sortable%2A) to the xref:Microsoft.AspNetCore.Components.QuickGrid.PropertyColumn%602 tag:

<PropertyColumn Property="..." Sortable="true" />

In the running app, sort the QuickGrid column by selecting the rendered column title.

Page items with a Paginator component

The QuickGrid component can page data from the data source. In Blazor Web Apps, paging requires the component to adopt an interactive render mode.

Add a xref:Microsoft.AspNetCore.Components.QuickGrid.PaginationState instance to the component's @code block. Set the xref:Microsoft.AspNetCore.Components.QuickGrid.PaginationState.ItemsPerPage%2A to the number of items to display per page. In the following example, the instance is named pagination, and ten items per page is set:

PaginationState pagination = new PaginationState { ItemsPerPage = 10 };

Set the QuickGrid component's xref:Microsoft.AspNetCore.Components.QuickGrid.QuickGrid`1.Pagination property to pagination:

<QuickGrid Items="..." Pagination="pagination">

To provide a UI for pagination, add a Paginator component above or below the QuickGrid component. Set the xref:Microsoft.AspNetCore.Components.QuickGrid.Paginator.State%2A?displayProperty=nameWithType to pagination:

<Paginator State="pagination" />

In the running app, page through the items using a rendered Paginator component.

QuickGrid renders additional empty rows to fill in the final page of data when used with a Paginator component. In .NET 9 or later, empty data cells (<td></td>) are added to the empty rows. The empty rows are intended to facilitate rendering the QuickGrid with stable row height and styling across all pages.

Apply row styles

Apply styles to rows using CSS isolation, which can include styling empty rows for QuickGrid components that page data with a Paginator component.

Wrap the QuickGrid component in a wrapper block element, for example a <div>:

+ <div>
    <QuickGrid ...>
        ...
    </QuickGrid>
+ </div>

Apply a row style with the ::deep pseudo-element. In the following example, row height is set to 2em, including for empty data rows.

{COMPONENT}.razor.css:

::deep tr {
    height: 2em;
}

Alternatively, use the following CSS styling approach:

  • Display row cells populated with data.
  • Don't display empty row cells, which avoids empty row cell borders from rendering per Bootstrap styling.

{COMPONENT}.razor.css:

::deep tr:has(> td:not(:empty)) > td {
    display: table-cell;
}

::deep td:empty {
    display: none;
}

For more information on using ::deep pseudo-elements with CSS isolation, see xref:blazor/components/css-isolation#child-component-support.

Custom attributes and styles

QuickGrid also supports passing custom attributes and style classes (xref:Microsoft.AspNetCore.Components.QuickGrid.QuickGrid%601.Class%2A) to the rendered table element:

<QuickGrid Items="..." custom-attribute="value" Class="custom-class">

Entity Framework Core (EF Core) data source

Use the factory pattern to resolve an EF Core database context that provides data to a QuickGrid component. For more information on why the factory pattern is recommended, see xref:blazor/blazor-ef-core.

A database context factory (xref:Microsoft.EntityFrameworkCore.IDbContextFactory%601) is injected into the component with the @inject directive. The factory approach requires disposal of the database context, so the component implements the xref:System.IAsyncDisposable interface with the @implements directive. The item provider for the QuickGrid component is a DbSet<T> obtained from the created database context (xref:Microsoft.EntityFrameworkCore.IDbContextFactory%601.CreateDbContext%2A) of the injected database context factory.

QuickGrid recognizes EF-supplied xref:System.Linq.IQueryable instances and knows how to resolve queries asynchronously for efficiency.

Add a package reference for the Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter NuGet package.

[!INCLUDE]

Call xref:Microsoft.Extensions.DependencyInjection.EntityFrameworkAdapterServiceCollectionExtensions.AddQuickGridEntityFrameworkAdapter%2A on the service collection in the Program file to register an EF-aware xref:Microsoft.AspNetCore.Components.QuickGrid.IAsyncQueryExecutor implementation:

builder.Services.AddQuickGridEntityFrameworkAdapter();

The following example uses an ExampleTable xref:Microsoft.EntityFrameworkCore.DbSet%601 (table) from a AppDbContext database context (context) as the data source for a QuickGrid component:

@using Microsoft.AspNetCore.Components.QuickGrid
@using Microsoft.EntityFrameworkCore
@implements IAsyncDisposable
@inject IDbContextFactory<AppDbContext> DbFactory

...

<QuickGrid ... Items="context.ExampleTable" ...>
    ...
</QuickGrid>

@code {
    private AppDbContext context = default!;

    protected override void OnInitialized()
    {
        context = DbFactory.CreateDbContext();
    }

    public async ValueTask DisposeAsync() => await context.DisposeAsync();
}

In the code block (@code) of the preceding example:

  • The context field holds the database context, typed as an AppDbContext.
  • The OnInitialized lifecycle method assigns a new database context (xref:Microsoft.EntityFrameworkCore.IDbContextFactory%601.CreateDbContext%2A) to the context field from the injected factory (DbFactory).
  • The asynchronous DisposeAsync method disposes of the database context when the component is disposed.

You may also use any EF-supported LINQ operator to filter the data before passing it to the xref:Microsoft.AspNetCore.Components.QuickGrid.QuickGrid%601.Items%2A parameter.

The following example filters movies by a movie title entered in a search box. The database context is BlazorWebAppMoviesContext, and the model is Movie. The movie's Title property is used for the filter operation.

@using Microsoft.AspNetCore.Components.QuickGrid
@using Microsoft.EntityFrameworkCore
@implements IAsyncDisposable
@inject IDbContextFactory<BlazorWebAppMoviesContext> DbFactory

...

<p>
    <input type="search" @bind="titleFilter" @bind:event="oninput" />
</p>

<QuickGrid ... Items="FilteredMovies" ...>
    ...
</QuickGrid>

@code {
    private string titleFilter = string.Empty;
    private BlazorWebAppMoviesContext context = default!;

    protected override void OnInitialized()
    {
        context = DbFactory.CreateDbContext();
    }

    private IQueryable<Movie> FilteredMovies => 
        context.Movie.Where(m => m.Title!.Contains(titleFilter));

    public async ValueTask DisposeAsync() => await context.DisposeAsync();
}

For a working example, see the following resources:

Display name support

A column title can be assigned using xref:Microsoft.AspNetCore.Components.QuickGrid.ColumnBase%601.Title?displayProperty=nameWithType in the xref:Microsoft.AspNetCore.Components.QuickGrid.PropertyColumn`2's tag. In the following movie example, the column is given the name "Release Date" for the column's movie release date data:

<PropertyColumn Property="movie => movie.ReleaseDate" Title="Release Date" />

However, managing column titles (names) from bound model properties is usually a better choice for maintaining an app. A model can control the display name of a property with the [Display] attribute. In the following example, the model specifies a movie release date display name of "Release Date" for its ReleaseDate property:

[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }

To enable the QuickGrid component to use the xref:System.ComponentModel.DataAnnotations.DisplayAttribute.Name?displayProperty=nameWithType, subclass xref:Microsoft.AspNetCore.Components.QuickGrid.PropertyColumn`2 either in the component or in a separate class:

public class DisplayNameColumn<TGridItem, TProp> : PropertyColumn<TGridItem, TProp>
{
    protected override void OnParametersSet()
    {
        if (Title is null && Property.Body is MemberExpression memberExpression)
        {
            var memberInfo = memberExpression.Member;
            Title = 
                memberInfo.GetCustomAttribute<DisplayNameAttribute>().DisplayName ??
                memberInfo.GetCustomAttribute<DisplayAttribute>().Name ??
                memberInfo.Name;
        }

        base.OnParametersSet();
    }
}

Use the subclass in the QuickGrid component. In the following example, the preceding DisplayNameColumn is used. The name "Release Date" is provided by the [Display] attribute in the model, so there's no need to specify a xref:Microsoft.AspNetCore.Components.QuickGrid.ColumnBase%601.Title:

<DisplayNameColumn Property="movie => movie.ReleaseDate" />

The [DisplayName] attribute is also supported:

[DisplayName("Release Date")]
public DateTime ReleaseDate { get; set; }

However, the [Display] attribute is recommended because it makes additional properties available. For example, the [Display] attribute offers the ability to assign a resource type for localization.

Remote data

In Blazor WebAssembly apps, fetching data from a JSON-based web API on a server is a common requirement. To fetch only the data that's required for the current page/viewport of data and apply sorting or filtering rules on the server, use the xref:Microsoft.AspNetCore.Components.QuickGrid.QuickGrid%601.ItemsProvider%2A parameter.

xref:Microsoft.AspNetCore.Components.QuickGrid.QuickGrid%601.ItemsProvider%2A can also be used in a server-side Blazor app if the app is required to query an external endpoint or in other cases where the requirements aren't covered by an xref:System.Linq.IQueryable.

Supply a callback matching the xref:Microsoft.AspNetCore.Components.QuickGrid.GridItemsProvider%601 delegate type, where TGridItem is the type of data displayed in the grid. The callback is given a parameter of type xref:Microsoft.AspNetCore.Components.QuickGrid.GridItemsProviderRequest%601, which specifies the start index, maximum row count, and sort order of data to return. In addition to returning the matching items, a total item count (totalItemCount) is also required for paging and virtualization to function correctly.

The following example obtains data from the public OpenFDA Food Enforcement database.

The xref:Microsoft.AspNetCore.Components.QuickGrid.GridItemsProvider%601 converts the xref:Microsoft.AspNetCore.Components.QuickGrid.GridItemsProviderRequest%601 into a query against the OpenFDA database. Query parameters are translated into the particular URL format supported by the external JSON API. It's only possible to perform sorting and filtering via sorting and filtering that's supported by the external API. The OpenFDA endpoint doesn't support sorting, so none of the columns are marked as sortable. However, it does support skipping records (skip parameter) and limiting the return of records (limit parameter), so the component can enable virtualization and scroll quickly through tens of thousands of records.

FoodRecalls.razor:

@page "/food-recalls"
@inject HttpClient Http
@inject NavigationManager Navigation

<PageTitle>Food Recalls</PageTitle>

<h1>OpenFDA Food Recalls</h1>

<div class="grid" tabindex="-1">
    <QuickGrid ItemsProvider="@foodRecallProvider" Virtualize="true">
        <PropertyColumn Title="ID" Property="@(c => c.Event_Id)" />
        <PropertyColumn Property="@(c => c.State)" />
        <PropertyColumn Property="@(c => c.City)" />
        <PropertyColumn Title="Company" Property="@(c => c.Recalling_Firm)" />
        <PropertyColumn Property="@(c => c.Status)" />
    </QuickGrid>
</div>

<p>Total: <strong>@numResults results found</strong></p>

@code {
    private GridItemsProvider<FoodRecall>? foodRecallProvider;
    private int numResults;

    protected override async Task OnInitializedAsync()
    {
        foodRecallProvider = async req =>
        {
            var url = Navigation.GetUriWithQueryParameters(
                "https://api.fda.gov/food/enforcement.json", 
                new Dictionary<string, object?>
            {
                { "skip", req.StartIndex },
                { "limit", req.Count },
            });

            var response = await Http.GetFromJsonAsync<FoodRecallQueryResult>(
                url, req.CancellationToken);

            return GridItemsProviderResult.From(
                items: response!.Results,
                totalItemCount: response!.Meta.Results.Total);
        };

        numResults = (await Http.GetFromJsonAsync<FoodRecallQueryResult>(
            "https://api.fda.gov/food/enforcement.json"))!.Meta.Results.Total;
    }
}

For more information on calling web APIs, see xref:blazor/call-web-api.

QuickGrid scaffolder

The QuickGrid scaffolder scaffolds Razor components with QuickGrid to display data from a database.

The scaffolder generates basic Create, Read, Update, and Delete (CRUD) pages based on an Entity Framework Core data model. You can scaffold individual pages or all of the CRUD pages. You select the model class and the DbContext, optionally creating a new DbContext if needed.

The scaffolded Razor components are added to the project's in a generated folder named after the model class. The generated Index component uses a QuickGrid component to display the data. Customize the generated components as needed and enable interactivity to take advantage of interactive features, such as paging, sorting and filtering.

The components produced by the scaffolder require server-side rendering (SSR), so they aren't supported when running on WebAssembly.

Visual Studio

Right-click on the Components/Pages folder and select Add > New Scaffolded Item.

With the Add New Scaffold Item dialog open to Installed > Common > Blazor > Razor Component, select Razor Components using Entity Framework (CRUD). Select the Add button.

CRUD is an acronym for Create, Read, Update, and Delete. The scaffolder produces create, edit, delete, details, and index components for the app.

Complete the Add Razor Components using Entity Framework (CRUD) dialog:

  • The Template dropdown list includes other templates for specifically creating create, edit, delete, details, and list components. This dropdown list comes in handy when you only need to create a specific type of component scaffolded to a model class. Leave the Template dropdown list set to CRUD to scaffold a full set of components.
  • In the Model class dropdown list, select the model class. A folder is created for the generated components from the model name (if the model class is named Movie, the folder is automatically named MoviePages).
  • For DbContext class, select an existing database context or select the + (plus sign) button and Add Data Context modal dialog to add a new database context.
  • After the model dialog closes, the Database provider dropdown list defaults to SQL Server. You can select the appropriate provider for the database that you're using. The options include SQL Server, SQLite, PostgreSQL, and Azure Cosmos DB.
  • Select Add.

Visual Studio Code

Paste all of the following commands at the prompt (>) of the Terminal (Terminal menu > New Terminal) opened to the project's root directory. When you paste multiple commands, a warning appears stating that multiple commands will execute. Dismiss the warning and proceed with the paste operation.

When you paste multiple commands, all of the commands execute except the last one. The last command doesn't execute until you press Enter on the keyboard.

dotnet tool install --global dotnet-aspnet-codegenerator
dotnet tool install --global dotnet-ef
dotnet add package Microsoft.EntityFrameworkCore.SQLite
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.AspNetCore.Components.QuickGrid
dotnet add package Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter

[!IMPORTANT] After the first eight commands execute, make sure that you press Enter on the keyboard to execute the last command.

The preceding commands add:

In the Terminal, execute the following command to scaffold a full set of components with the CRUD template:

dotnet aspnet-codegenerator blazor CRUD -dbProvider {PROVIDER} -dc {DB CONTEXT CLASS} -m {MODEL} -outDir {PATH}

[!NOTE] The preceding command is a .NET CLI command, and .NET CLI commands are executed when entered at a PowerShell prompt, which is the default command shell of the VS Code Terminal.

The following table explains the ASP.NET Core code generator options in the preceding command.

Option Placeholder Description
-dbProvider {PROVIDER} Database provider to use. Options include sqlserver (default), sqlite, cosmos, postgres.
-dc {DB CONTEXT CLASS} The xref:Microsoft.EntityFrameworkCore.DbContext class to use, including the namespace.
-m {MODEL} The name of the model class.
-outDir {PATH} The output directory for the generated components. A folder is created from the model name in the output directory to hold the components (if the model class is named Movie, the folder is automatically named MoviePages). The path is typically either Components/Pages for a Blazor Web App or Pages for a standalone Blazor WebAssembly app.

For the additional Blazor provider options, use the .NET CLI help option (-h|--help):

dotnet aspnet-codegenerator blazor -h

.NET CLI

Paste all of the following commands at the prompt (>) of a command shell opened to the project's root directory. When you paste multiple commands, a warning appears stating that multiple commands will execute. Dismiss the warning and proceed with the paste operation.

When you paste multiple commands, all of the commands execute except the last one. The last command doesn't execute until you press Enter on the keyboard.

dotnet tool install --global dotnet-aspnet-codegenerator
dotnet tool install --global dotnet-ef
dotnet add package Microsoft.EntityFrameworkCore.SQLite
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.AspNetCore.Components.QuickGrid
dotnet add package Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter

[!IMPORTANT] After the first eight commands execute, make sure that you press Enter on the keyboard to execute the last command.

The preceding commands add:

In a command shell, execute the following command to scaffold a full set of components with the CRUD template:

dotnet aspnet-codegenerator blazor CRUD -dbProvider {PROVIDER} -dc {DB CONTEXT CLASS} -m {MODEL} -outDir {PATH}

The following table explains the ASP.NET Core code generator options in the preceding command.

Option Placeholder Description
-dbProvider {PROVIDER} Database provider to use. Options include sqlserver (default), sqlite, cosmos, postgres.
-dc {DB CONTEXT CLASS} The xref:Microsoft.EntityFrameworkCore.DbContext class to use, including the namespace.
-m {MODEL} The name of the model class.
-outDir {PATH} The output directory for the generated components. A folder is created from the model name in the output directory to hold the components (if the model class is named Movie, the folder is automatically named MoviePages). The path is typically either Components/Pages for a Blazor Web App or Pages for a standalone Blazor WebAssembly app.

For the additional Blazor provider options, use the .NET CLI help option (-h|--help):

dotnet aspnet-codegenerator blazor -h

For an example use of the QuickGrid scaffolder, see xref:blazor/tutorials/movie-database-app/index.