ASP.NET Core BlazorQuickGrid
组件
注意
此版本不是本文的最新版本。 有关当前版本,请参阅本文的 .NET 9 版本。
QuickGrid
组件是一个 Razor 组件,用于以表格格式快速高效地显示数据。 QuickGrid
为常见网格呈现方案提供简单方便的数据网格组件,并充当构建数据网格组件的参考体系结构和性能基线。 QuickGrid
进行了高度的优化,并使用高级技术实现最佳呈现性能。
程序包
为 Microsoft.AspNetCore.Components.QuickGrid
包添加包引用。
注意
有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。
示例应用
有关各种 QuickGrid
演示,请参阅面向 Blazor 示例应用的快速入门。 演示网站托管在 GitHub Pages 上。 由于使用社区维护的 BlazorWasmPrerendering.Build
GitHub 项目进行静态预呈现,因此站点加载速度较快。
QuickGrid
实现
若要实现 QuickGrid
组件,请执行以下操作:
- 在
QuickGrid
标记 (Razor) 中指定<QuickGrid>...</QuickGrid>
组件的标记。 - 为网格命名可查询数据源。 使用以下任一数据源:
- Items:可以为 null 的
IQueryable<TGridItem>
,其中TGridItem
是网格中每一行表示的数据类型。 - ItemsProvider:为网格提供数据的回调。
- Items:可以为 null 的
- Class:可选的 CSS 类名称。 如果已提供,则类名称将包含在呈现的表的
class
属性中。 - Theme:主题名称(默认值:
default
)。 这会影响哪些样式规则与表匹配。 - Virtualize:如果为 true,则网格会使用虚拟化呈现。 这通常与滚动结合使用,导致网格仅提取和呈现当前滚动视区周围的数据。 这可以极大地提高滚动浏览大型数据集时的性能。 如果使用 Virtualize,则应为 ItemSize 提供值,并且必须确保每一行以恒定的高度呈现。 通常,如果呈现的数据量较小或使用的是分页,最好不要使用 Virtualize。
- ItemSize:仅当使用 Virtualize 时才适用。 ItemSize 定义每一行的预期高度(以像素为单位),使虚拟化机制能够提取正确数量的项,以匹配显示大小并确保准确滚动。
- ItemKey:(可选)在呈现的每一行上为
@key
定义值。 通常,这用于为每个数据项指定唯一标识符,例如主键值。 这样,网格就可以根据唯一标识符保留行元素和数据项之间的关联,即使TGridItem
实例被新副本替换(例如,在对基础数据存储执行新查询之后)也是如此。 如果未设置,则@key
为TGridItem
实例。 - OverscanCount:定义在可见区域前后呈现以减少滚动期间的呈现频率的其他项数。 虽然较高的值可通过在屏幕外呈现更多项来提高滚动平滑度,但较高的值也会导致初始加载时间增加。 建议根据数据集大小和用户体验要求找到平衡点。 默认值为 3。 仅在使用 Virtualize 时可用。
- Pagination:(可选)将此
TGridItem
实例与 PaginationState 模型链接,使网格仅提取并呈现数据的当前页。 这通常与 Paginator 组件或显示和更新提供的 PaginationState 实例的一些其他 UI 逻辑结合使用。 - 在
QuickGrid
子内容 (RenderFragment) 中,指定 PropertyColumn<TGridItem,TProp>,表示单元格显示值的TGridItem
列:- Property:定义要在此列的单元格中显示的值。
- Format:(可选)指定值的格式字符串。 使用 Format 需要
TProp
类型来实现 IFormattable。 - Sortable:指示是否应按此列对数据进行排序。 默认值可能因列类型而异。 例如,如果指定了任何 TemplateColumn<TGridItem> 参数,则对 SortBy 进行排序。
- InitialSortDirection:如果 IsDefaultSortColumn 为
true
,则指示排序方向。 - IsDefaultSortColumn:指示默认情况下是否应对此列进行排序。
- PlaceholderTemplate:如果已指定,虚拟化网格使用此模板呈现尚未加载数据的单元格。
- HeaderTemplate:此列标头单元格的可选模板。 如果未指定,则默认标头模板包括 Title,还包括任何适用的排序指示器和选项按钮。
- Title:列的标题文本。 如果未使用 HeaderTemplate,标题将自动呈现。
- 在
QuickGrid
标记 (Razor) 中指定<QuickGrid>...</QuickGrid>
组件的标记。 - 为网格命名可查询数据源。 使用以下任一数据源:
- Items:可以为 null 的
IQueryable<TGridItem>
,其中TGridItem
是网格中每一行表示的数据类型。 - ItemsProvider:为网格提供数据的回调。
- Items:可以为 null 的
- Class:可选的 CSS 类名称。 如果已提供,则类名称将包含在呈现的表的
class
属性中。 - Theme:主题名称(默认值:
default
)。 这会影响哪些样式规则与表匹配。 - Virtualize:如果为 true,则网格会使用虚拟化呈现。 这通常与滚动结合使用,导致网格仅提取和呈现当前滚动视区周围的数据。 这可以极大地提高滚动浏览大型数据集时的性能。 如果使用 Virtualize,则应为 ItemSize 提供值,并且必须确保每一行以恒定的高度呈现。 通常,如果呈现的数据量较小或使用的是分页,最好不要使用 Virtualize。
- ItemSize:仅当使用 Virtualize 时才适用。 ItemSize 定义每一行的预期高度(以像素为单位),使虚拟化机制能够提取正确数量的项,以匹配显示大小并确保准确滚动。
- ItemKey:(可选)在呈现的每一行上为
@key
定义值。 通常,这用于为每个数据项指定唯一标识符,例如主键值。 这样,网格就可以根据唯一标识符保留行元素和数据项之间的关联,即使TGridItem
实例被新副本替换(例如,在对基础数据存储执行新查询之后)也是如此。 如果未设置,则@key
为TGridItem
实例。 - Pagination:(可选)将此
TGridItem
实例与 PaginationState 模型链接,使网格仅提取并呈现数据的当前页。 这通常与 Paginator 组件或显示和更新提供的 PaginationState 实例的一些其他 UI 逻辑结合使用。 - 在
QuickGrid
子内容 (RenderFragment) 中,指定 PropertyColumn<TGridItem,TProp>,表示单元格显示值的TGridItem
列:- Property:定义要在此列的单元格中显示的值。
- Format:(可选)指定值的格式字符串。 使用 Format 需要
TProp
类型来实现 IFormattable。 - Sortable:指示是否应按此列对数据进行排序。 默认值可能因列类型而异。 例如,如果指定了任何 TemplateColumn<TGridItem> 参数,则对 SortBy 进行排序。
- InitialSortDirection:如果 IsDefaultSortColumn 为
true
,则指示排序方向。 - IsDefaultSortColumn:指示默认情况下是否应对此列进行排序。
- PlaceholderTemplate:如果已指定,虚拟化网格使用此模板呈现尚未加载数据的单元格。
- HeaderTemplate:此列标头单元格的可选模板。 如果未指定,则默认标头模板包括 Title,还包括任何适用的排序指示器和选项按钮。
- Title:列的标题文本。 如果未使用 HeaderTemplate,标题将自动呈现。
例如,添加以下组件以呈现网格。
对于 Blazor Web App,QuickGrid
组件必须采用交互式呈现模式才能启用分页和排序等交互式功能。
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();
}
在浏览器中的相对路径 /promotion-grid
处访问组件。
目前没有使用完全成熟的商业网格倾向于提供的功能扩展 QuickGrid
的计划,例如分层行、拖动以重新排序列或类似于 Excel 的范围选择。 如果需要不希望自行开发的高级功能,请继续使用第三方网格。
按列排序
QuickGrid
组件可以按列对项进行排序。 在 Blazor Web App中,排序要求组件采用交互式呈现模式。
将 Sortable="true"
(Sortable) 添加到 PropertyColumn<TGridItem,TProp> 标记:
<PropertyColumn Property="..." Sortable="true" />
在正在运行的应用中,通过选择呈现的列标题对 QuickGrid
列进行排序。
包含 Paginator
组件的页项
QuickGrid
组件可以从数据源中对数据进行分页。 在 Blazor Web App 中,分页要求组件采用交互式呈现模式。
将 PaginationState 实例添加到组件的 @code
块中。 将 ItemsPerPage 设置为每页要显示的项数。 在以下示例中,实例命名为 pagination
,每页项数设置为 10:
PaginationState pagination = new PaginationState { ItemsPerPage = 10 };
将 QuickGrid
组件的 Pagination 属性设置为 pagination
:
<QuickGrid Items="..." Pagination="pagination">
若要提供分页的 UI,请在 Paginator
组件上方或下方添加 QuickGrid
。 将 Paginator.State 设置为 pagination
:
<Paginator State="pagination" />
在正在运行的应用中,使用呈现的 Paginator
组件分页浏览项。
QuickGrid 呈现其他空行,以在与 Paginator
组件一起使用时填充数据的最后一页。 在 .NET 9 或更高版本中,空数据单元格(<td></td>
)将添加到空行。 空行旨在方便呈现具有稳定行高度的 QuickGrid,并在所有页面中设置样式。
应用行样式
使用 CSS 隔离对行应用样式,其中包括为 QuickGrid
组件设置空行的样式,这些组件 具有 Paginator
组件的页面数据。
将 QuickGrid
组件包装在包装块元素中,例如 <div>
:
+ <div>
<QuickGrid ...>
...
</QuickGrid>
+ </div>
使用 ::deep
伪元素应用行样式。 在以下示例中,行高设置为 2em
,包括空数据行。
{COMPONENT}.razor.css
:
::deep tr {
height: 2em;
}
或者,使用以下 CSS 样式方法:
- 显示填充数据的行单元格。
- 不要显示空行单元格,这样可以避免按照 Bootstrap 样式规则显示空行单元格边框。
{COMPONENT}.razor.css
:
::deep tr:has(> td:not(:empty)) > td {
display: table-cell;
}
::deep td:empty {
display: none;
}
有关在 CSS 隔离中使用 ::deep
伪元素 的详细信息,请参阅 ASP.NET Core Blazor CSS 隔离。
自定义属性和样式
QuickGrid 还支持将自定义属性和样式类 (Class) 传递给呈现的表元素:
<QuickGrid Items="..." custom-attribute="value" Class="custom-class">
Entity Framework Core (EF Core) 数据源
使用工厂模式解析向 EF Core 组件提供数据的 QuickGrid
数据库上下文。 若要详细了解为什么推荐使用工厂模式,请参阅使用 Entity Framework Core (Blazor) 的 ASP.NET Core EF Core。
使用 IDbContextFactory<TContext> 指令将数据库上下文工厂 (@inject
) 注入组件。 工厂方法需要处置数据库上下文,因此组件使用 IAsyncDisposable 指令实现 @implements
接口。 QuickGrid
组件的项提供程序是一个 DbSet<T>
,从注入的数据库上下文工厂已创建的数据库上下文 (CreateDbContext) 获取。
QuickGrid 可识别 EF 提供的 IQueryable 实例,并知道如何异步解析查询以提高效率。
添加 Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter
NuGet 包的包引用。
注意
有关将包添加到 .NET 应用的指南,请参阅包使用工作流(NuGet 文档)中“安装和管理包”下的文章。 在 NuGet.org 中确认正确的包版本。
调用 AddQuickGridEntityFrameworkAdapter 文件中服务集合上的 Program
,以注册 EF 感知 IAsyncQueryExecutor 实现:
builder.Services.AddQuickGridEntityFrameworkAdapter();
以下示例使用 ExampleTable
数据库上下文 (DbSet<TEntity>) 中的 AppDbContext
context
(表)作为 QuickGrid
组件的数据源:
@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();
}
在上述示例的代码块 (@code
) 中:
context
字段保留数据库上下文,作为AppDbContext
键入。OnInitialized
生命周期方法将新的数据库上下文 (CreateDbContext) 分配给注入工厂 (context
) 中的DbFactory
字段。- 异步
DisposeAsync
方法在组件被释放时释放数据库上下文。
还可以使用任何 EF 支持的 LINQ 运算符来筛选数据,然后再将其传递给 Items 参数。
以下示例按在搜索框中输入的电影标题筛选电影。 数据库上下文是 BlazorWebAppMoviesContext
,模型是 Movie
。 电影的 Title
属性用于筛选操作。
@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();
}
有关工作示例,请参阅以下资源:
- 生成 Blazor 电影数据库应用教程
- Blazor 电影数据库示例应用:选择存储库中的最新版本文件夹。 本教程项目的示例文件夹命名
BlazorWebAppMovies
。
显示名称支持
可以使用 ColumnBase<TGridItem>.Title 的标记中的 PropertyColumn<TGridItem,TProp> 来分配列标题。 在下面的电影示例中,为列的影片发布日期数据指定了名称“Release Date
”:
<PropertyColumn Property="movie => movie.ReleaseDate" Title="Release Date" />
但是,要维护应用,更好的选择是从绑定模型属性管理列标题(名称)。 模型可以使用 [Display]
特性控制属性的显示名称。 在以下示例中,模型为其 Release Date
属性指定影片发布日期显示名称“ReleaseDate
”:
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
若要使 QuickGrid
组件能够使用 DisplayAttribute.Name,请将 PropertyColumn<TGridItem,TProp> 设置为组件或单独的类中的子类:
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();
}
}
使用 QuickGrid
组件中的子类。 在以下示例中,使用了上述 DisplayNameColumn
。 名称“Release Date
”由模型中的 [Display]
特性提供,因此无需指定 Title:
<DisplayNameColumn Property="movie => movie.ReleaseDate" />
还支持 [DisplayName]
特性:
[DisplayName("Release Date")]
public DateTime ReleaseDate { get; set; }
但是,建议使用 [Display]
特性,因为它使其他属性可用。 例如,通过 [Display]
特性可以为本地化分配资源类型。
远程数据
在 Blazor WebAssembly 应用中,从服务器上基于 JSON 的 Web API 获取数据是一项常见要求。 若要仅提取当前页/数据视区所需的数据,并在服务器上应用排序或筛选规则,请使用 ItemsProvider 参数。
在服务器端 ItemsProvider 应用中,如果需要应用来查询外部终结点,或者在 Blazor 无法满足要求的其他情况下,也可以使用 IQueryable。
提供与 GridItemsProvider<TGridItem> 委托类型匹配的回调,其中 TGridItem
是网格中显示的数据类型。 该回调被赋予一个 GridItemsProviderRequest<TGridItem> 类型的参数,它指定了要返回的数据的起始索引、最大行计数和排序顺序。 除了返回匹配项之外,分页和虚拟化还需要总项计数 (totalItemCount
) 才能正常运行。
以下示例从公开的 OpenFDA Food Enforcement 数据库获取数据。
GridItemsProvider<TGridItem> 将 GridItemsProviderRequest<TGridItem> 转换为针对 OpenFDA 数据库的查询。 查询参数转换为外部 JSON API 支持的特定 URL 格式。 只能通过外部 API 支持的排序和筛选来执行排序和筛选。 OpenFDA 终结点不支持排序,因此没有任何列标记为可排序。 但是,它确实支持跳过记录(skip
参数)和限制返回记录的数量(limit
参数),以便组件可以启用虚拟化,并快速滚动浏览数万条记录。
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;
}
}
有关调用 Web API 的详细信息,请参阅在 ASP.NET Core Blazor 应用中调用 Web API。
QuickGrid
基架
QuickGrid
基架用于为包含 Razor 的 QuickGrid
组件搭建基架,以显示数据库中的数据。
基架基于 Entity Framework Core 数据模型生成基本的创建、读取、更新和删除 (CRUD) 页。 可以搭建单个页面或所有 CRUD 页面。 选择模型类和 DbContext
,根据需要创建新的 DbContext
。
搭建的 Razor 组件会添加到生成的文件夹(以模型类命名)中项目的文件夹内。 生成的 Index
组件使用 QuickGrid
组件显示数据。 根据需要自定义生成的组件,并使交互性能够利用交互式功能,例如分页、排序和筛选。
基架生成的组件需要服务器端呈现 (SSR),因此在 WebAssembly 上运行时不支持它们。
右键单击 Components/Pages
文件夹,然后选择“添加”“已搭建基架的新项”>。
打开“添加新基架项”对话框,转到“已安装”“通用”“>Razor。 选择“添加”按钮。
CRUD 是创建 (Create)、读取 (Read)、更新 (Update) 和删除 (Delete) 的首字母缩写。 基架生成应用的创建、编辑、删除、详细信息和索引组件。
完成“添加使用实体框架(CRUD)的 Razor 组件”对话框:
- “模板”下拉列表包含用于专门创建“编辑”、“创建”、“删除”、“详细信息”和“列出”组件的其他模板。 如果只需要创建已搭建为某个模型类的特定组件类型,此下拉列表就非常有用。 将“模板”下拉列表设置为“CRUD”,以搭建一组完整的组件。
- 在“模型类”下拉列表中,选择模型类。 根据模型名称为生成的组件创建一个文件夹(如果模型类命名为
Movie
,则文件夹会自动命名为MoviePages
)。 - 对于 DbContext 类,请选择现有数据库上下文或依次选择“”(加号)按钮和“添加数据上下文”模式对话框来添加新数据库上下文+。
- 模型对话框关闭后,“数据库提供程序”下拉列表默认为 SQL Server。 可以为正在使用的数据库选择适当的提供程序。 这些选项包括 SQL Server、SQLite、PostgreSQL 和 Azure Cosmos DB。
- 选择 添加 。
有关基架的示例用法QuickGrid
,请参阅生成Blazor电影数据库应用(概述)。