public interface IPagerDataSource
{
int TotalRecords { get; }
IEnumerable GetPageData(int pageIndex, int pageSize);
}
public class PagerControl : WebControl, INamingContainer
{
// 核心屬性
public int PageSize { get; set; } = 10;
public int CurrentPageIndex { get; private set; } = 0;
public int TotalRecords { get; private set; }
public int TotalPages => (int)Math.Ceiling((double)TotalRecords / PageSize);
// 外觀屬性
public string CssClass { get; set; } = "pager";
public string FirstPageText { get; set; } = "首頁";
public string PrevPageText { get; set; } = "上一頁";
public string NextPageText { get; set; } = "下一頁";
public string LastPageText { get; set; } = "末頁";
public bool ShowPageNumbers { get; set; } = true;
public int PageNumberCount { get; set; } = 10;
// 事件
public event EventHandler PageChanged;
// 資料來源設定
public IPagerDataSource DataSource { get; set; }
// 生命週期
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (DataSource != null)
{
TotalRecords = DataSource.TotalRecords;
// 處理頁碼傳遞 (QueryString/PostBack)
string pageArg = Page.Request.QueryString["page"] ?? Page.Request.Form["pager_page"];
if (int.TryParse(pageArg, out int idx) && idx >= 0 && idx < TotalPages)
CurrentPageIndex = idx;
}
EnsureChildControls();
}
protected override void CreateChildControls()
{
Controls.Clear();
if (TotalPages <= 1) return; // 資料敏感:單頁隱藏
var ul = new HtmlGenericControl("ul") { Attributes = { ["class"] = CssClass } };
Controls.Add(ul);
// 首頁/上一頁
AddButton(ul, "first", FirstPageText, 0, CurrentPageIndex == 0);
AddButton(ul, "prev", PrevPageText, CurrentPageIndex - 1, CurrentPageIndex == 0);
// 頁碼
if (ShowPageNumbers)
{
int start = Math.Max(0, CurrentPageIndex - PageNumberCount / 2);
int end = Math.Min(TotalPages - 1, start + PageNumberCount - 1);
start = Math.Max(0, end - PageNumberCount + 1);
for (int i = start; i <= end; i++)
AddButton(ul, "num", (i + 1).ToString(), i, false, i == CurrentPageIndex);
}
// 下一頁/末頁
AddButton(ul, "next", NextPageText, CurrentPageIndex + 1, CurrentPageIndex == TotalPages - 1);
AddButton(ul, "last", LastPageText, TotalPages - 1, CurrentPageIndex == TotalPages - 1);
}
private void AddButton(Control container, string type, string text, int pageIndex, bool disabled, bool active = false)
{
var li = new HtmlGenericControl("li");
if (disabled) li.Attributes["class"] = "disabled";
if (active) li.Attributes["class"] = (li.Attributes["class"] ?? "") + " active";
var a = new HtmlAnchor { HRef = $"?page={pageIndex}", InnerText = text };
if (disabled) a.Attributes["onclick"] = "return false;";
li.Controls.Add(a);
container.Controls.Add(li);
}
protected virtual void OnPageChanged(PageChangedEventArgs e)
{
PageChanged?.Invoke(this, e);
}
}
public class PageChangedEventArgs : EventArgs
{
public int NewPageIndex { get; set; }
public int PageSize { get; set; }
}
<%@ Register TagPrefix="cc" Namespace="MyControls" Assembly="MyControls" %>
<cc:PagerControl ID="Pager1" runat="server" PageSize="10"
FirstPageText="<<" PrevPageText="<" NextPageText=">" LastPageText=">>"
OnPageChanged="Pager1_PageChanged" />
<asp:Repeater ID="rptData" runat="server">
<ItemTemplate>
<div><%# Eval("Title") %></div>
</ItemTemplate>
</asp:Repeater>
public partial class DemoPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Pager1.DataSource = new ArticlePagerSource(); // 實作 IPagerDataSource
Pager1.PageChanged += Pager1_PageChanged;
BindData();
}
}
private void BindData()
{
var data = ((IPagerDataSource)Pager1.DataSource).GetPageData(Pager1.CurrentPageIndex, Pager1.PageSize);
rptData.DataSource = data;
rptData.DataBind();
}
protected void Pager1_PageChanged(object sender, PageChangedEventArgs e)
{
BindData();
}
}
public class ArticlePagerSource : IPagerDataSource
{
public int TotalRecords => DbHelper.ExecuteScalar("SELECT COUNT(*) FROM Articles WHERE IsPublished=1");
public IEnumerable GetPageData(int pageIndex, int pageSize)
{
string sql = @"
SELECT * FROM Articles
WHERE IsPublished=1
ORDER BY PublishDate DESC
OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY";
return DbHelper.Query(sql, new { Offset = pageIndex * pageSize, PageSize = pageSize });
}
}
.pager { display:flex; list-style:none; padding:0; gap:4px; justify-content:center; }
.pager li a { display:block; padding:6px 12px; border:1px solid #ddd; border-radius:3px; color:#333; text-decoration:none; }
.pager li.active a { background:#007bff; color:#fff; border-color:#007bff; }
.pager li.disabled a { color:#999; pointer-events:none; background:#f5f5f5; }
通用分頁控件核心在於:介面抽象資料來源、`IPagerDataSource` 解耦分頁邏輯與資料存取;資料敏感渲染自動隱藏無意義按鈕;事件驅動通知顯示控件重新繫結。此架構可輕鬆擴充 AJAX 分頁、無限滾動、URL 友善路由等進階功能。