This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Friday, 07 November 2025
即将到来,只是把 完成的触摸到它。 跟着GitHub走! .
这只是为了让你看到我在这个控制系统上的进展!
在经过数月的进化和来自社区的宝贵反馈(5.7k+下载! )之后,我很兴奋地宣布PagageTagHelfer图书馆已经达到了1.0.0版本。 这不仅仅是一个版本数字突撞 — — 它代表图书馆完全成熟,具有适合现实世界、生产应用程序的特点。
如果你一直跟着这个系列, 你会记得我们从 光骨头呼叫加,加,加,加,加,加 可排序排序标题,并抽取 页面大小控件1.0.0版本采纳了我们学到的一切知识,并增加了关键的企业特征:
让我们潜入其中的每一个特征 看看他们如何合作 创造出一个真正灵活的方位解决方案
与 SQL 数据库一起,传统页形工作非常出色,您可以很容易地在 SQL 数据库中工作 SKIP 和 TAKE 但是,如果你与诺斯QL数据库合作,比如宇宙DB、DynamoDB或Azure表格存储等数据库合作,会发生什么情况?这些数据库不支持基于抵消的页码 — — 相反,它们使用 续序号.
以下是持续象征性的外观与传统传呼的不同之处:
graph TD
A[Traditional Paging] --> B[Page 1: OFFSET 0 LIMIT 10]
A --> C[Page 2: OFFSET 10 LIMIT 10]
A --> D[Page 3: OFFSET 20 LIMIT 10]
E[Token-Based Paging] --> F[Page 1: No token]
F --> G[Returns: Data + Token_A]
G --> H[Page 2: Token_A]
H --> I[Returns: Data + Token_B]
I --> J[Page 3: Token_B]
style A stroke:#0ea5e9,stroke-width:3px
style E stroke:#ec4899,stroke-width:3px
传统传呼:
基于 Tokon 的调制调频呼叫 :
新的 <continuation-pager> 标签标签工具帮助使执行基于象征性的页光法简单化。 首先,创建一个执行模式 IContinuationPagingModel:
public class ProductPagingViewModel : IContinuationPagingModel
{
public string? NextPageToken { get; set; }
public bool HasMoreResults { get; set; }
public int PageSize { get; set; } = 25;
public int CurrentPage { get; set; } = 1;
public Dictionary<int, string>? PageTokenHistory { get; set; }
public ViewType ViewType { get; set; } = ViewType.TailwindAndDaisy;
// Your actual data
public List<Product> Products { get; set; } = new();
}
界面微乎其微,但功能强大。让我们看看每个属性都做什么:
NextPageToken: 获取下一页的标记(由您的数据库提供)HasMoreResults: 布尔表示是否有更多页面PageSize:每页项目数CurrentPage: UI 的仅显示页码PageTokenHistory: 用于后向导航的符号的词典映射页面编号ViewType: CSS 框架用于制作现在让我们执行一个模拟 Cosmos DB 风格的控制器动作 :
[Route("Products")]
public async Task<IActionResult> Products(
int currentPage = 1,
int pageSize = 25,
string? pageToken = null,
string? tokenHistory = null)
{
// Simulate fetching from Cosmos DB
var cosmosResults = await _cosmosService.GetProductsAsync(
pageSize: pageSize,
continuationToken: pageToken
);
// Deserialize token history for backward navigation
var history = string.IsNullOrEmpty(tokenHistory)
? new Dictionary<int, string>()
: JsonSerializer.Deserialize<Dictionary<int, string>>(tokenHistory)
?? new Dictionary<int, string>();
// Store current token in history
if (!string.IsNullOrEmpty(pageToken))
{
history[currentPage] = pageToken;
}
var viewModel = new ProductPagingViewModel
{
CurrentPage = currentPage,
PageSize = pageSize,
NextPageToken = cosmosResults.ContinuationToken,
HasMoreResults = cosmosResults.HasMoreResults,
PageTokenHistory = history,
Products = cosmosResults.Items
};
if (Request.IsHtmx())
{
return PartialView("_ProductList", viewModel);
}
return View(viewModel);
}
此执行显示了象征性历史是如何使后向导航得以实现的。 没有它, 继续象征性的显示只会支持“ 下一步 ” 按钮 。 通过维持页面对位映射字典, 我们可以支持“ 前一” 和“ 下一步 ” 导航 。
以下是象征性积累流的可视化图象:
sequenceDiagram
participant User
participant Controller
participant Database
participant TokenHistory
User->>Controller: Request Page 1 (no token)
Controller->>Database: Query with no token
Database-->>Controller: Data + Token_A
Controller->>TokenHistory: Store Token_A for page 1
Controller-->>User: Display Page 1
User->>Controller: Request Page 2 (Token_A)
Controller->>Database: Query with Token_A
Database-->>Controller: Data + Token_B
Controller->>TokenHistory: Add Token_B for page 2
Controller-->>User: Display Page 2
User->>Controller: Request Page 1 (retrieve from history)
Controller->>TokenHistory: Get Token for Page 1
Controller->>Database: Query with Token_A
Database-->>Controller: Data + Token_A
Controller-->>User: Display Page 1
在您的 Razor 视图中, 使用续序寻呼器很简单 :
@model ProductPagingViewModel
<div id="product-container">
<table class="table">
<thead>
<tr>
<th>Product</th>
<th>Company</th>
<th>Price</th>
</tr>
</thead>
<tbody>
@foreach (var product in Model.Products)
{
<tr>
<td>@product.Name</td>
<td>@product.CompanyName</td>
<td>[email protected]("N2")</td>
</tr>
}
</tbody>
</table>
<continuation-pager
model="Model"
htmx-target="#product-container"
show-page-number="true"
show-pagesize="true" />
</div>
标签助手自动 :
HasMoreResults 是假的象征性历史方法的天才在于它是完全可选的。 如果您只需要“ 下一步” 导航( 例如无限滚动) , 您可以完全忽略象征性历史 :
<continuation-pager
model="Model"
enable-token-accumulation="false"
show-page-number="false" />
这样就成了“ 下一个” 按钮, 没有页面指示器或历史管理 。
对于完整导航, 符号历史会自动在查询字符串中作为 JSON 序列序列。 下面是 URL 与符号历史相似 :
/Products?currentPage=3&pageSize=25&pageToken=abc123&tokenHistory=%7B%221%22%3A%22xyz789%22%2C%222%22%3A%22abc123%22%7D
缩略 tokenHistory 参数包含编码字典,使后向导航无缝。
续用传呼器最重要的UX改进之一是 编号页面按钮。在您向前浏览时,页机为所有访问过的页面显示可点击的页码:
Initial page 1: [Next →]
After next click: [← Prev] [1] [2 active] [3 disabled] [Next →]
After next click: [← Prev] [1] [2] [3 active] [4 disabled] [Next →]
Click page 2: [← Prev] [1] [2 active] [3] [4 disabled] (no next - not visited yet)
这提供了传统的页码 UX, 并同时保持了基于符号的后端结构。 执行系统为每个访问的页面备有标牌, 允许直接浏览之前访问过的页面 。
限制历史增长:
为防止无约束内存使用,设置 max-history-pages (违约:20):
<continuation-pager
model="Model"
max-history-pages="50"
show-page-number="true" />
当限制达到时,最古老的页面符号会自动缩写 。
这是继续传呼器实施的最重要特点。
继续代号只有与生成该代号的相同的查询上下文( 过滤器、 种类、 搜索) 有效。 使用带有不同查询参数的代号将返回错误的数据或完全失败 。
续用 pelcer 自动保存所有查询参数, 除了它自己的参数以外 :
<!-- URL with filters -->
/Products?category=electronics&brand=acme&minPrice=100
<!-- After clicking Next -->
/Products?category=electronics&brand=acme&minPrice=100¤tPage=2&pageToken=xyz123&tokenHistory={...}
<!-- All filters preserved! Token is valid because query context matches. -->
需要时您可以禁用此行为 :
<continuation-pager
model="Model"
preserve-query-parameters="false" />
但是,这是 强烈不鼓励 除非您绝对确定您的标语不取决于查询背景。
为何如此重要:
宇宙 DB 示例:
// Page 1 with filter
var query = container.GetItemQueryIterator<Product>(
"SELECT * FROM c WHERE c.category = 'electronics'",
continuationToken: null
);
var response = await query.ReadNextAsync();
// Returns: Products + Token_A
// Page 2 with SAME filter - Token_A is valid
var query2 = container.GetItemQueryIterator<Product>(
"SELECT * FROM c WHERE c.category = 'electronics'",
continuationToken: Token_A // ✅ Works!
);
// Page 2 with DIFFERENT filter - Token_A is invalid
var query3 = container.GetItemQueryIterator<Product>(
"SELECT * FROM c WHERE c.category = 'computers'",
continuationToken: Token_A // ❌ Wrong results or error!
);
连续传呼机的自动参数保存确保标记始终与原始查询上下文一起使用。
现代应用软件为全球受众服务,外形控制需要讲您的用户语言。 版本 1. 0.0 包括设置在图书馆中的全面本地化支持。
译为8种语言的图书馆船只:
代号: 语言: 语言: 语言:
|------|----------|
| en 英文( 默认)
| de 德语( 德文)
| es 西班牙语(埃斯帕尼奥尔语)
| fr 法语( 法国)
| it 意大利语( 意大利语)
| pt 葡萄牙语( 葡萄牙文)
| ja 日语 ()
| zh-Hans 中文简化 ()
所有文本均本地化,包括:
本地化系统由 .resx 资源文件, 方便添加您自己的语言。 所有资源文件都在 mostlylucid.pagingtaghelper/Resources/.
使用本地化是直截了当的。 language 属性 :
<paging
model="Model"
language="de"
show-summary="true"
first-last-navigation="true" />
这样所有案文都用德文写成:
<!-- Previous button -->
<button>‹ Vorherige</button>
<!-- Summary -->
<div class="text-sm text-gray-600">
Zeige 1 bis 10 von 256 Einträgen
</div>
<!-- Next button -->
<button>Nächste ›</button>
对于基于用户首选项的动态语言切换,请在您的控制器中设定语言 :
public async Task<IActionResult> Products(
int page = 1,
int pageSize = 10,
string language = "en")
{
var pagingModel = await GenerateModel(page, pageSize);
ViewBag.SelectedLanguage = language;
return View(pagingModel);
}
然后在您看来, 创建一个语言选择器 :
@{
var selectedLanguage = ViewBag.SelectedLanguage as string ?? "en";
var languages = new Dictionary<string, string>
{
{ "en", "English" },
{ "de", "German" },
{ "es", "Spanish" },
{ "fr", "French" },
{ "it", "Italian" },
{ "pt", "Portuguese" },
{ "ja", "Japanese" },
{ "zh-Hans", "Chinese" }
};
}
<select onchange="window.location.href='/Products?language=' + this.value">
@foreach (var lang in languages)
{
<option value="@lang.Key" selected="@(lang.Key == selectedLanguage)">
@lang.Value
</option>
}
</select>
<paging
model="Model"
language="@selectedLanguage"
link-url="/Products" />
您还可以在仍然受益于其它元素本地化的同时, 推翻单个文本字符串 :
<paging
model="Model"
language="ja"
previous-page-text="戻る"
next-page-text="次へ"
summary-template="全{TotalItems}件中 {StartItem}~{EndItem}件を表示" />
缩略 PagingLocalizer 服务会自动处理特定文化的格式。如果您通过了无效的语言代码,它会优雅地返回到英语中。
对于 HTMX 整合, 您会想要保存语言, 而不是请求 :
<script>
htmx.on('htmx:configRequest', function(event) {
if (event.detail.path.includes('/Products')) {
event.detail.parameters.language = '@selectedLanguage';
}
});
</script>
这样可以确保HTMX部分视图更新保留所选语文。
在 v1. 0.0 中最重要的改进之一是采用灵活的 JavaScript 模式。 以前, 你有一个布林选择 : use-htmx="true" 或 use-htmx="false"。现在,你有五种不同的模式,每种模式都为不同的情景优化。
以下是 JavaScript 模式的完整解析 :
graph TD
A[JavaScript Modes] --> B[HTMX]
A --> C[HTMXWithAlpine]
A --> D[Alpine]
A --> E[PlainJS]
A --> F[NoJS]
B --> B1[Uses HTMX for partial updates]
B --> B2[hx-get, hx-target, hx-swap]
C --> C1[HTMX + Alpine.js directives]
C --> C2[Enhanced interactivity]
D --> D1[Pure Alpine.js]
D --> D2[x-data, @click handlers]
E --> E1[Vanilla JavaScript]
E --> E2[onclick handlers]
F --> F1[Zero JavaScript]
F --> F2[Standard anchor links & forms]
让我们看看每个模式的动作 :
1. HTMX 模式 (违约)
<paging
model="Model"
js-mode="HTMX"
htmx-target="#results-container" />
重命名 :
<button hx-get="/Products?page=2" hx-target="#results-container" hx-swap="outerHTML">
Next ›
</button>
适合动态页面更新, 无需重新加载整页。 这是现代 ASP. NET 核心应用程序的建议模式 。
2. HTMX随高山模式
<paging
model="Model"
js-mode="HTMXWithAlpine"
htmx-target="#results-container" />
重命名 :
<button
x-data
hx-get="/Products?page=2"
hx-target="#results-container"
hx-swap="outerHTML">
Next ›
</button>
与 Alpine.js 组合 HTMX, 用于导航, 用于额外的客户端互动。 当您需要反应性 UI 元素时, 请使用它与页码( 装入指示器、 动画、 客户端验证) 一同使用 。
3. 高山模式
<paging
model="Model"
js-mode="Alpine" />
重命名 :
<button
x-data
@click="window.location.href = '/Products?page=2'">
Next ›
</button>
纯Alpine.js没有HTMX。 当你已经使用Alpine.js但不想使用HTMX依赖性时, 有用。
4. 普通JS 模式
<paging
model="Model"
js-mode="PlainJS" />
重命名 :
<button onclick="window.location.href = '/Products?page=2'">
Next ›
</button>
无框架依赖性, 只是 vanilla JavaScript。 此模式还包括用于更改页面大小的助手 :
@Html.PageSizeOnchangeSnippet()
这将输入所需的 JavaScript , 用于在不使用 HTMX 的情况下处理页面大小下拉修改 。
5. 无 JS 模式
<paging
model="Model"
js-mode="NoJS" />
重命名 :
<!-- Navigation uses standard anchor links -->
<a href="/Products?page=2">Next ›</a>
<!-- Page size uses a form with submit button -->
<form method="get" action="/Products">
<input type="hidden" name="page" value="1" />
<select name="pageSize" onchange="this.form.submit()">
<option value="10">10</option>
<option value="25" selected>25</option>
<option value="50">50</option>
</select>
<noscript>
<button type="submit">Update</button>
</noscript>
</form>
需要零 JavaScript 。 适合 :
这个体系的美丽之处在于 所有模式都保存您现有的查询参数。无论您是按类别、搜索或排序过滤,页码都自动保持状态。
对于后向兼容性,旧的 use-htmx 属性仍然工作 :
<!-- Old syntax (still works) -->
<paging model="Model" use-htmx="true" />
<!-- Equivalent to js-mode="HTMX" -->
<paging model="Model" use-htmx="false" />
<!-- Equivalent to js-mode="PlainJS" -->
不过,我建议移到新的 js-mode 清晰度属性 :
<!-- New syntax (recommended) -->
<paging model="Model" js-mode="HTMX" />
<paging model="Model" js-mode="PlainJS" />
1.0.0版本引入了两个重要的ViewType添加内容,涉及共同的现实世界情景。
前情前情提要 如果你想要尾风CS的外型 TailwindAndDaisy 视图使用 DaisiUI 组件。 如果您已经使用 DaisyUI 组件, 这很好, 但如果您想要纯尾风, 没有 DaisyUI 依赖关系 ?
输入 ViewType.Tailwind:
<paging
model="Model"
view-type="Tailwind" />
这样就只能使用标准的尾风通用类:
<div class="flex gap-2 items-center">
<button class="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700">
‹ Previous
</button>
<div class="px-3 py-1 text-sm font-medium bg-gray-100 dark:bg-gray-700 dark:text-white rounded-md">
Page 1
</div>
<button class="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700">
Next ›
</button>
</div>
否 无 btn, badge,或 join 类 - 只是纯粹的尾风。 这使您能够完全控制没有组件库依赖的外形。
比较:
|----------|---------------|-------------------|----------|
| TailwindAndDaisy 使用 DaisyUI 的項目 : DaisywindCSS : DaisiUI : DaisyUI : DaisyUI : DaisywindCSS : Daisui : DaisyUI : DaisyUI :
| Tailwind
| Bootstrap
| Plain * 嵌入的CSS * 无 * 没有框架依赖 *
| NoJS 嵌入的CSS 零 零 JavaScript 要求
缩略 NoJS ViewType 组合零 JavaScript 和 普通 CSS 格式 :
<paging
model="Model"
view-type="NoJS"
show-pagesize="true" />
与其他视图类型的关键差异 :
<a href="/Products?page=2" class="pager-button">Next ›</a>
<form method="get" action="/Products" class="page-size-form">
<!-- Preserves all current query parameters as hidden inputs -->
<input type="hidden" name="search" value="laptop" />
<input type="hidden" name="category" value="electronics" />
<!-- Reset to page 1 when changing page size -->
<input type="hidden" name="page" value="1" />
<label for="pageSize">Items per page:</label>
<select name="pageSize" onchange="this.form.submit()">
<option value="10">10</option>
<option value="25" selected>25</option>
<option value="50">50</option>
</select>
<!-- Button visible when JavaScript is disabled -->
<noscript>
<button type="submit" class="page-size-button">Update</button>
</noscript>
</form>
缩略 onchange="this.form.submit()" 当 JavaScript 有时提供方便, 但 <noscript> 按钮确保不完全功能时的完整功能。
平面执行中最令人沮丧的一个方面是失去您的过滤器、搜索条件或页面间导航时排序顺序。 1. 0.0 版本以优雅的方式解决了这个问题。 自动保存所有查询参数, 除了射向控制器自己的参数.
此特性在 正常的呼叫器和续续的呼叫器和横 全部 JavaScript 模式和视图类型.
以下是内部运作方式:
string BuildQueryString(string? token, int page)
{
var query = new Dictionary<string, string>();
// Define continuation pager's own parameters that should be excluded from preservation
var pagerParams = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"pageSize", "currentPage", "pageToken", "tokenHistory"
};
// Add parameter prefix variants if using prefixed parameters
if (!string.IsNullOrEmpty(Model.ParameterPrefix))
{
pagerParams.Add($"{Model.ParameterPrefix}_pageSize");
pagerParams.Add($"{Model.ParameterPrefix}_currentPage");
pagerParams.Add($"{Model.ParameterPrefix}_pageToken");
pagerParams.Add($"{Model.ParameterPrefix}_tokenHistory");
}
// Preserve all existing query parameters (except pager's own) if enabled
if (Model.PreserveQueryParameters)
{
foreach (var param in ViewContext.HttpContext.Request.Query)
{
if (!pagerParams.Contains(param.Key))
{
query[param.Key] = param.Value.ToString();
}
}
}
// Add continuation pager parameters (with prefix if specified)
var pageSizeParam = Model.GetParameterName("pageSize");
var currentPageParam = Model.GetParameterName("currentPage");
var pageTokenParam = Model.GetParameterName("pageToken");
var tokenHistoryParam = Model.GetParameterName("tokenHistory");
query[pageSizeParam] = pageSize.ToString();
query[currentPageParam] = page.ToString();
if (!string.IsNullOrEmpty(token))
query[pageTokenParam] = token;
if (Model.EnableTokenAccumulation)
query[tokenHistoryParam] = tokenHistoryJson;
return string.Join("&", query.Select(kvp =>
$"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value)}"));
}
这种办法意味着:
假设情景1:搜索+铺边
Initial URL: /Products?search=laptop&category=electronics&page=1
Click Next: /Products?search=laptop&category=electronics&page=2
Change Page Size: /Products?search=laptop&category=electronics&page=1&pageSize=50
假设情景2:排序+铺边
Initial URL: /Products?orderBy=price&descending=true&page=1
Click Page 3: /Products?orderBy=price&descending=true&page=3
设想方案3:带过滤器的连续页面器
Initial URL: /Products?category=electronics&brand=acme
Click Next: /Products?category=electronics&brand=acme¤tPage=2&pageToken=abc123&tokenHistory={...}
相同的保存在窗体中工作( NoJS 模式) 。 在创建页面大小窗体时, 视图自动包含所有非粘贴参数的隐藏输入 :
<form method="get" action="@linkUrl" class="page-size-form">
@* Preserve all existing query parameters except pageSize and page-related ones *@
@foreach (var param in ViewContext.HttpContext.Request.Query)
{
if (!new[] { "pageSize", "currentPage", "pageToken", "tokenHistory" }
.Contains(param.Key, StringComparer.OrdinalIgnoreCase))
{
<input type="hidden" name="@param.Key" value="@param.Value" />
}
}
@* Reset to page 1 when changing page size *@
<input type="hidden" name="currentPage" value="1" />
<select name="pageSize" onchange="this.form.submit()">
<!-- options -->
</select>
</form>
这工作无缝无缝 全部 JavaScript 模式和所有视图类型。您不必手动管理查询字符串传播。
升级为1.0版前版本是直截了当的,但有一些突破性的变化需要了解。
1. use-htmx 已经折旧(但仍在工作)
旧 :
<paging model="Model" use-htmx="true" />
<paging model="Model" use-htmx="false" />
新的(建议的新):
<paging model="Model" js-mode="HTMX" />
<paging model="Model" js-mode="PlainJS" />
2. ViewType.Taillwind和Daisy现在使用完整的DaisiUI组件
如果你使用 ViewType.TailwindAndDaisy 并想要纯粹的尾风 没有DisaiUI:
古老的行为( 纯粹的尾风) :
<paging model="Model" view-type="TailwindAndDaisy" />
(以获得旧行为):
<paging model="Model" view-type="Tailwind" />
继续使用 TailwindAndDaisy 如果您使用 DisaiUI 组件 :
<paging model="Model" view-type="TailwindAndDaisy" />
<!-- Uses btn, join, badge, select, etc. -->
3. HTMX升级为2.0.4
如果您在应用程序的其他地方使用HTMX, 请确保与 HTMX 2. 4. 的兼容性。 大多数 HTMX 1. x 代码未变, 但审查 HTMX2.0 移徙指南 边缘情况。
第1步:更新 NuGet 软件包
dotnet add package mostlylucid.pagingtaghelper --version 1.0.0
步骤2:审查现行代码
搜索您的代码库 use-htmx 属性 :
# PowerShell
Get-ChildItem -Recurse -Include *.cshtml | Select-String "use-htmx"
# Bash/Git Bash
grep -r "use-htmx" --include="*.cshtml" .
步骤3:更新js模式(建议)
替换 use-htmx 与 js-mode:
- <paging model="Model" use-htmx="true" htmx-target="#results" />
+ <paging model="Model" js-mode="HTMX" htmx-target="#results" />
- <paging model="Model" use-htmx="false" />
+ <paging model="Model" js-mode="PlainJS" />
步骤4:审查尾风和Daisy的使用情况
如果你没有安装DaisheUI,但正在使用 TailwindAndDaisy:
- <paging model="Model" view-type="TailwindAndDaisy" />
+ <paging model="Model" view-type="Tailwind" />
步骤5:彻底测试
运行您的应用程序和测试 :
一旦移民,考虑采用这些新的特征:
本地化:
<paging
model="Model"
language="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName" />
继续 Pager( 如果使用 NoSQL ) :
<continuation-pager
model="Model"
htmx-target="#results-container"
show-page-number="true" />
NoJS 模式( 用于无障碍 ) :
<paging model="Model" js-mode="NoJS" />
图书馆包含一个全面的演示应用程序, 显示所有功能。 您可以在本地运行或查看 。 演示点站点 (即将到来)。
本地管理演示 :
git clone https://github.com/scottgal/mostlylucid.pagingtaghelper.git
cd mostlylucid.pagingtaghelper/mostlylucid.pagingtaghelper.sample
dotnet run
导航至 https://localhost:5001 要探索:
每个演示包括:
该图书馆经过战斗测试,下载了1.7k+的下载,可供制作使用,所有106个单元测试通过,综合演示应用程序展示了现实世界的使用模式。
我正在考虑:
通过 NuGet 安装 :
dotnet add package mostlylucid.pagingtaghelper --version 1.0.0
检查文档 :
问题、反馈或贡献? @scottgal (苏格兰语).
欢乐快乐!
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.