Notikumi
Power BI DataViz pasaules čempionāts
14. febr. 16 - 31. marts 16
Ar 4 iespējām piedalīties, jūs varētu laimēt konferences paketi un iekļūt LIVE Grand Finale Lasvegasā
Uzzināt vairākŠī pārlūkprogramma vairs netiek atbalstīta.
Jauniniet uz Microsoft Edge, lai izmantotu jaunāko līdzekļu, drošības atjauninājumu un tehniskā atbalsta sniegtās priekšrocības.
QuickGrid
componentPiezīme
This isn't the latest version of this article. For the current release, see the .NET 9 version of this article.
Svarīgi
This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
For the current release, see the .NET 9 version of this article.
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.
Add a package reference for the Microsoft.AspNetCore.Components.QuickGrid
package.
Piezīme
For guidance on adding packages to .NET apps, see the articles under Install and manage packages at Package consumption workflow (NuGet documentation). Confirm correct package versions at NuGet.org.
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.
To implement a QuickGrid
component:
QuickGrid
component in Razor markup (<QuickGrid>...</QuickGrid>
).IQueryable<TGridItem>
, where TGridItem
is the type of data represented by each row in the grid.class
attribute of the rendered table.default
). This affects which styling rules match the table.@key
on each rendered row. Typically, this is used to specify a unique identifier, such as a primary key value, for each data item. This allows the grid to preserve the association between row elements and data items based on their unique identifiers, even when the TGridItem
instances are replaced by new copies (for example, after a new query against the underlying data store). If not set, the @key
is the TGridItem
instance.TGridItem
instance with a PaginationState model, causing the grid to fetch and render only the current page of data. This is normally used in conjunction with a Paginator component or some other UI logic that displays and updates the supplied PaginationState instance.TGridItem
columns whose cells display values:
TProp
type to implement IFormattable.true
.QuickGrid
component in Razor markup (<QuickGrid>...</QuickGrid>
).IQueryable<TGridItem>
, where TGridItem
is the type of data represented by each row in the grid.class
attribute of the rendered table.default
). This affects which styling rules match the table.@key
on each rendered row. Typically, this is used to specify a unique identifier, such as a primary key value, for each data item. This allows the grid to preserve the association between row elements and data items based on their unique identifiers, even when the TGridItem
instances are replaced by new copies (for example, after a new query against the underlying data store). If not set, the @key
is the TGridItem
instance.TGridItem
instance with a PaginationState model, causing the grid to fetch and render only the current page of data. This is normally used in conjunction with a Paginator component or some other UI logic that displays and updates the supplied PaginationState instance.TGridItem
columns whose cells display values:
TProp
type to implement IFormattable.true
.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
:
@page "/promotion-grid"
@using Microsoft.AspNetCore.Components.QuickGrid
<PageTitle>Promotion Grid</PageTitle>
<h1>Promotion Grid Example</h1>
<QuickGrid Items="people">
<PropertyColumn Property="@(p => p.PersonId)" Sortable="true" />
<PropertyColumn Property="@(p => p.Name)" Sortable="true" />
<PropertyColumn Property="@(p => p.PromotionDate)" Format="yyyy-MM-dd" Sortable="true" />
</QuickGrid>
@code {
private record Person(int PersonId, string Name, DateOnly PromotionDate);
private IQueryable<Person> people = new[]
{
new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)),
new Person(10944, "António Langa", new DateOnly(1991, 12, 1)),
new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)),
new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)),
new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)),
new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)),
}.AsQueryable();
}
@page "/promotion-grid"
@using Microsoft.AspNetCore.Components.QuickGrid
<PageTitle>Promotion Grid</PageTitle>
<h1>Promotion Grid Example</h1>
<QuickGrid Items="people">
<PropertyColumn Property="@(p => p.PersonId)" Sortable="true" />
<PropertyColumn Property="@(p => p.Name)" Sortable="true" />
<PropertyColumn Property="@(p => p.PromotionDate)" Format="yyyy-MM-dd" Sortable="true" />
</QuickGrid>
@code {
private record Person(int PersonId, string Name, DateOnly PromotionDate);
private IQueryable<Person> people = new[]
{
new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)),
new Person(10944, "António Langa", new DateOnly(1991, 12, 1)),
new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)),
new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)),
new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)),
new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)),
}.AsQueryable();
}
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.
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"
(Sortable) to the PropertyColumn<TGridItem,TProp> tag:
<PropertyColumn Property="..." Sortable="true" />
In the running app, sort the QuickGrid column by selecting the rendered column title.
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 PaginationState instance to the component's @code
block. Set the ItemsPerPage 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 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 Paginator.State 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 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:
{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 ASP.NET Core Blazor CSS isolation.
QuickGrid also supports passing custom attributes and style classes (Class) to the rendered table element:
<QuickGrid Items="..." custom-attribute="value" Class="custom-class">
Apply a stylesheet class to a row of the grid based on the row item using the RowClass
parameter.
In the following example:
Person
record. The Person
record includes a FirstName
property.GetRowCssClass
method applies the highlight-row
class styles to any row where the person's first name is "Julie
."<QuickGrid ... RowClass="GetRowCssClass">
...
</QuickGrid>
@code {
private record Person(int PersonId, string FirstName, string LastName);
private string GetRowCssClass(Person person) =>
person.FirstName == "Julie" ? "highlight-row" : null;
}
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 ASP.NET Core Blazor with Entity Framework Core (EF Core).
A database context factory (IDbContextFactory<TContext>) is injected into the component with the @inject
directive. The factory approach requires disposal of the database context, so the component implements the IAsyncDisposable interface with the @implements
directive. The item provider for the QuickGrid
component is a DbSet<T>
obtained from the created database context (CreateDbContext) of the injected database context factory.
QuickGrid recognizes EF-supplied IQueryable instances and knows how to resolve queries asynchronously for efficiency.
Add a package reference for the Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter
NuGet package.
Piezīme
For guidance on adding packages to .NET apps, see the articles under Install and manage packages at Package consumption workflow (NuGet documentation). Confirm correct package versions at NuGet.org.
Call AddQuickGridEntityFrameworkAdapter on the service collection in the Program
file to register an EF-aware IAsyncQueryExecutor implementation:
builder.Services.AddQuickGridEntityFrameworkAdapter();
The following example uses an ExampleTable
DbSet<TEntity> (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:
context
field holds the database context, typed as an AppDbContext
.OnInitialized
lifecycle method assigns a new database context (CreateDbContext) to the context
field from the injected factory (DbFactory
).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 Items 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:
BlazorWebAppMovies
.A column title can be assigned using ColumnBase<TGridItem>.Title in the PropertyColumn<TGridItem,TProp>'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 DisplayAttribute.Name property, subclass PropertyColumn<TGridItem,TProp>, either in the component or in a separate class. Call the GetName method to return the localized DisplayAttribute.Name value if an unlocalized DisplayName ([DisplayName]
attribute) doesn't hold the value:
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>().GetName() ??
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 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.
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 ItemsProvider parameter.
ItemsProvider 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 IQueryable.
Supply a callback matching the GridItemsProvider<TGridItem> delegate type, where TGridItem
is the type of data displayed in the grid. The callback is given a parameter of type GridItemsProviderRequest<TGridItem>, 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 GridItemsProvider<TGridItem> converts the GridItemsProviderRequest<TGridItem> 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 Call a web API from an ASP.NET Core Blazor app.
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.
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:
Movie
, the folder is automatically named MoviePages
).For an example use of the QuickGrid scaffolder, see Build a Blazor movie database app (Overview).
Multiple concurrent EF Core queries can trigger the following System.InvalidOperationException:
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
This scenario is scheduled for improvement in an upcoming release of ASP.NET Core. For more information, see [Blazor] Improve the experience with QuickGrid and EF Core (dotnet/aspnetcore
#58716).
In the meantime, you can address the problem using an ItemsProvider with a cancellation token. The cancellation token prevents concurrent queries by cancelling the previous request when a new request is issued.
Consider the following example, which is based on the movie database Index
component for the Build a Blazor movie database app (Overview) tutorial. The simpler version scaffolded into the app can be seen in the article's sample app. The Index
component scaffolded into the app is replaced by the following component.
Components/Pages/MoviePages/Index.razor
:
@page "/movies"
@rendermode InteractiveServer
@using Microsoft.EntityFrameworkCore
@using Microsoft.AspNetCore.Components.QuickGrid
@using BlazorWebAppMovies.Models
@using BlazorWebAppMovies.Data
@inject IDbContextFactory<BlazorWebAppMovies.Data.BlazorWebAppMoviesContext> DbFactory
<PageTitle>Index</PageTitle>
<h1>Index</h1>
<div>
<input type="search" @bind="titleFilter" @bind:event="oninput" />
</div>
<p>
<a href="movies/create">Create New</a>
</p>
<div>
<QuickGrid Class="table" TGridItem="Movie" ItemsProvider="GetMovies"
ItemKey="(x => x.Id)" Pagination="pagination">
<PropertyColumn Property="movie => movie.Title" Sortable="true" />
<PropertyColumn Property="movie => movie.ReleaseDate" Title="Release Date" />
<PropertyColumn Property="movie => movie.Genre" />
<PropertyColumn Property="movie => movie.Price" />
<PropertyColumn Property="movie => movie.Rating" />
<TemplateColumn Context="movie">
<a href="@($"movies/edit?id={movie.Id}")">Edit</a> |
<a href="@($"movies/details?id={movie.Id}")">Details</a> |
<a href="@($"movies/delete?id={movie.Id}")">Delete</a>
</TemplateColumn>
</QuickGrid>
</div>
<Paginator State="pagination" />
@code {
private BlazorWebAppMoviesContext context = default!;
private PaginationState pagination = new PaginationState { ItemsPerPage = 5 };
private string titleFilter = string.Empty;
public async ValueTask<GridItemsProviderResult<Movie>> GetMovies(GridItemsProviderRequest<Movie> request)
{
using var context = DbFactory.CreateDbContext();
var totalCount = await context.Movie.CountAsync(request.CancellationToken);
IQueryable<Movie> query = context.Movie.OrderBy(x => x.Id);
query = request.ApplySorting(query).Skip(request.StartIndex);
if (request.Count.HasValue)
{
query = query.Take(request.Count.Value);
}
var items = await query.ToArrayAsync(request.CancellationToken);
var result = new GridItemsProviderResult<Movie>
{
Items = items,
TotalItemCount = totalCount
};
return result;
}
}
ASP.NET Core atsauksmes
ASP.NET Core ir atklātā pirmkoda projekts. Atlasiet saiti, lai sniegtu atsauksmes:
Notikumi
Power BI DataViz pasaules čempionāts
14. febr. 16 - 31. marts 16
Ar 4 iespējām piedalīties, jūs varētu laimēt konferences paketi un iekļūt LIVE Grand Finale Lasvegasā
Uzzināt vairākApmācība
Modulis
Interact with data in Blazor web apps - Training
Learn how to create a graphical user interface in a Blazor web app by creating and assembling Blazor components. Access data and share it for display on multiple pages within your app.