在 ASP.NET WebForms 中,Repeater 控件不支援自動主從綁定,需手動在外層 ItemDataBound 事件中為內層 Repeater 指定數據源並綁定。本文提供完整的分類-產品兩級嵌套示例。
public partial class NestedRepeater : System.Web.UI.Page {
private DataTable dtCategory;
private DataTable dtProduct;
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
dtCategory = GetCategoryTable();
dtProduct = GetProductTable();
rptCategoryList.DataSource = dtCategory;
rptCategoryList.DataBind();
}
}
// 模擬分類表
private DataTable GetCategoryTable() {
var dt = new DataTable();
dt.Columns.Add("CategoryId", typeof(int));
dt.Columns.Add("CategoryTitle", typeof(string));
for (int i = 1; i <= 3; i++) {
dt.Rows.Add(i, "分類名字 " + i);
}
return dt;
}
// 模擬產品表
private DataTable GetProductTable() {
var dt = new DataTable();
dt.Columns.Add("ProductTitle", typeof(string));
dt.Columns.Add("CategoryId", typeof(int));
for (int i = 1; i <= 9; i++) {
int catId = i > 6 ? 3 : (i > 3 ? 2 : 1);
dt.Rows.Add("產品名字 " + i, catId);
}
return dt;
}
// 根據分類 ID 篩選產品
private DataTable GetProductTable(int categoryId) {
var dv = dtProduct.DefaultView;
dv.RowFilter = "CategoryId = " + categoryId;
return dv.ToTable();
}
// 外層 Repeater 綁定時,為內層指定數據源
protected void rptCategoryList_ItemDataBound(object sender, RepeaterItemEventArgs e) {
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) {
var drv = (DataRowView)e.Item.DataItem;
var ltlTitle = (Literal)e.Item.FindControl("ltlTitle");
ltlTitle.Text = drv["CategoryTitle"].ToString();
var rptProductList = (Repeater)e.Item.FindControl("rptProductList");
int catId = Convert.ToInt32(drv["CategoryId"]);
rptProductList.DataSource = GetProductTable(catId);
rptProductList.DataBind();
}
}
// 內層 Repeater 綁定產品標題
protected void rptProductList_ItemDataBound(object sender, RepeaterItemEventArgs e) {
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) {
var drv = (DataRowView)e.Item.DataItem;
var ltlTitle = (Literal)e.Item.FindControl("ltlTitle");
ltlTitle.Text = drv["ProductTitle"].ToString();
}
}
}
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="NestedRepeater.aspx.cs" Inherits="Demo.NestedRepeater" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Repeater 嵌套示例</title>
<style>
.listBox { margin-bottom: 20px; border: 1px solid #ddd; padding: 10px; }
.title { font-weight: bold; font-size: 1.1em; color: #333; margin-bottom: 10px; }
.content ul { list-style: none; padding-left: 20px; }
.content li { margin: 5px 0; color: #666; }
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Repeater ID="rptCategoryList" runat="server" OnItemDataBound="rptCategoryList_ItemDataBound">
<ItemTemplate>
<div class="listBox">
<div class="title">
<asp:Literal ID="ltlTitle" runat="server" />
</div>
<div class="content">
<ul>
<asp:Repeater ID="rptProductList" runat="server" OnItemDataBound="rptProductList_ItemDataBound">
<ItemTemplate>
<li><asp:Literal ID="ltlTitle" runat="server" /></li>
</ItemTemplate>
</asp:Repeater>
</ul>
</div>
</div>
</ItemTemplate>
</asp:Repeater>
</div>
</form>
</body>
</html>
Page_Load 只準備主表與從表完整數據,不進行關聯。CategoryId → 篩選從表 → 綁定內層 Repeater。e.Item.FindControl("rptProductList") 必須在 ItemTemplate 內查找。Dictionary<int, DataTable> 分類產品映射,避免重複 RowFilter。ListView + GroupTemplate 替代雙重 Repeater,語義更清晰。ViewState 時注意體積,嵌套繁多可關閉內層 EnableViewState="false"。| 錯誤 | 原因 | 修正 |
|---|---|---|
| 內層無數據 | 未在外層 ItemDataBound 中綁定內層 | 補全綁定邏輯 |
| FindControl 返回 null | ID 拼寫錯誤或控件不在命名容器內 | 檢查 ID、確保在 ItemTemplate 內 |
| 數據錯亂 | 使用全局變量未按行篩選 | 每行根據 CategoryId 篩選 |