1、安装插件
Sve.Blazor.InfiniteScroll
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>MauiApp2</title>
<base href="/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/app.css" rel="stylesheet" />
<link href="MauiApp2.styles.css" rel="stylesheet" />
</head>
<body>
<div class="status-bar-safe-area"></div>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webview.js" autostart="false"></script>
<script src="_content/Sve.Blazor.InfiniteScroll/js/Observer.js"></script>
</body>
</html>
2、添加页面
Result.razor
@page "/result"
@using Sve.Blazor.InfiniteScroll.Components
@inject IPoetryStorage _poetryStorage
<!-- 无限列表 -->
<InfiniteScroll ObserverTargetId="observerTarget" ObservableTargetReached=" _ => LoadMoreAsync()">
@foreach (var poetry in _poetries)
{
<div>
<Card>
<BodyTemplate>
<span style="font-weight:bold;color:#0078d4">@poetry.Name</span>
<div class="shim-2xs"></div>
@poetry.Dynasty @poetry.Author
<div class="shim-2xs"></div>
@poetry.Snipper
</BodyTemplate>
</Card>
</div>
<div class="shim-xl"></div>
}
<!--加载更多-->
<div id="observerTarget"></div>
</InfiniteScroll>
@code {
}
3、页面逻辑
Poetry.cs
using SQLite;
namespace MauiApp2.Library.Models;
[Table("works")]
public class Poetry
{
[Column("id"), PrimaryKey, AutoIncrement]
public int Id { get; set; }
[Column("name")]
public string? Name { get; set; }
[Column("author_name")]
public string Author { get; set; } = string.Empty;
[Column("dynasty")]
public string Dynasty { get; set; } = string.Empty;
[Column("content")]
public string Content { get; set; } = string.Empty;
/// <summary>
/// 内容摘要
/// </summary>
private string? _snippet;
/// <summary>
/// 正文首句
/// </summary>
[Ignore]
public string Snipper => _snippet ??= Content.Split("。")[0].Replace("\r\n", newValue: " ");
}
cResult.razor.cs
using System.Linq.Expressions;
using MauiApp2.Library.Models;
namespace MauiApp2.Library.Pages;
public partial class Result
{
/// <summary>
/// 返回查询结果
/// </summary>
private Expression<Func<Poetry, bool>> _where = p => true;
public const string Loading = "正在载入";
public const string NoResult = "没有满足条件的结果";
public const string NoMoreResult = "没有更多结果";
public const int PageSize = 20;
private string _status = string.Empty;
private List<Poetry> _poetries = new List<Poetry>();
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!firstRender) return;
// TODO 测试代码
await _poetryStorage.InitializeAsync();
await LoadMoreAsync();
}
private async Task LoadMoreAsync()
{
var poetries = await _poetryStorage.GetPoetriesAsync(_where, _poetries.Count, PageSize);
_poetries.AddRange(poetries);
}
}
4、项目路由
Main.razor
<Router AppAssembly="@typeof(Main).Assembly" AdditionalAssemblies="@(new []{typeof(Library.Models.Poetry).Assembly})">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
5、依赖注入
MauiProgram.cs
using MauiApp2.Data;
using MauiApp2.Library.Services;
using MauiApp2.Services;
using Microsoft.Extensions.Logging;
namespace MauiApp2
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
builder.Logging.AddDebug();
#endif
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddScoped<IPoetryStorage, PoetryStorage>();
builder.Services.AddScoped<IPreferenceStorage, PreferenceStorage>();
return builder.Build();
}
}
}