在 GridView 每一列顯示一個省份,並內嵌 CheckBoxList 列出該省份下的所有城市。使用者勾選省份 CheckBox 時,自動全選/取消該列城市;頁面頂部「全選」再遞迴控制所有省份與城市。
關鍵在於 **動態 ClientID 規律推導**。GridView 命名容器產生的 ID 格式為:
ctl00_ContentPlaceHolder1_gvItem_ctl{rowIndex}_CheckBox1
ctl00_ContentPlaceHolder1_gvItem_ctl{rowIndex}_gvInnerItem (內層 CheckBoxList)
<script type="text/javascript">
var gvPrefix = "ctl00_ContentPlaceHolder1_gvItem_";
function getRowId(rowIndex) {
// GridView 行索引從 0 起,ctl 編號從 2 起 (含 Header)
var n = rowIndex + 2;
return n < 10 ? "ctl0" + n : "ctl" + n;
}
// 省份 CheckBox 點擊 → 控制該列城市
function ProvinceClick(chk, rowIndex) {
var rowId = getRowId(rowIndex);
var cityListId = gvPrefix + rowId + "_gvInnerItem";
var cityInputs = document.getElementById(cityListId).getElementsByTagName("input");
for (var i = 0; i < cityInputs.length; i++)
cityInputs[i].checked = chk.checked;
}
// 全選/取消
function SelectAll(chkAll) {
var gv = document.getElementById(gvPrefix.replace("_gvItem_", "")); // 取得 GridView table
var provinceChecks = gv.getElementsByTagName("input");
for (var i = 0; i < provinceChecks.length; i++) {
var id = provinceChecks[i].id;
if (id.endsWith("_CheckBox1")) { // 省份 CheckBox
provinceChecks[i].checked = chkAll.checked;
// 觸發城市聯動
var rowIdx = parseInt(id.match(/ctl(\d+)_/)[1]) - 2;
if (rowIdx >= 0) ProvinceClick(provinceChecks[i], rowIdx);
}
}
}
</script>
<asp:GridView ID="gvItem" runat="server" AutoGenerateColumns="false"
OnRowDataBound="gvItem_RowDataBound" DataKeyNames="State">
<Columns>
<asp:TemplateField HeaderText="選擇省份">
<ItemTemplate>
<asp:CheckBox ID="CheckBox1" runat="server"
onclick='ProvinceClick(this, <%# Container.DataItemIndex %>)' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="State" HeaderText="省份名稱" />
<asp:TemplateField HeaderText="選擇城市">
<ItemTemplate>
<asp:CheckBoxList ID="gvInnerItem" runat="server"
RepeatLayout="Flow" RepeatDirection="Horizontal" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:CheckBox ID="chkAll" runat="server" Text="全選" onclick="SelectAll(this)" />
protected void gvItem_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
var cbl = (CheckBoxList)e.Row.FindControl("gvInnerItem");
string state = gvItem.DataKeys[e.Row.RowIndex].Value.ToString();
string sql = $"SELECT DISTINCT City FROM CityTable WHERE State = '{state}'";
DataTable dt = DbHelper.GetDataTable(sql); // 自行實作
cbl.DataSource = dt;
cbl.DataTextField = "City";
cbl.DataValueField = "City";
cbl.DataBind();
}
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
var selected = new List<string>();
foreach (GridViewRow row in gvItem.Rows)
{
var cbl = (CheckBoxList)row.FindControl("gvInnerItem");
foreach (ListItem item in cbl.Items)
{
if (item.Selected)
selected.Add($"{gvItem.DataKeys[row.RowIndex].Value}-{item.Value}");
}
}
// selected 即為 "省份-城市" 清單
}
ClientIDMode="Static" 或 Predictable,JS 選取器需相應調整。Sys.Application.add_load 或 PageRequestManager)。GridView 巢狀 CheckBoxList 透過 **命名容器 ID 規律 + JavaScript 聯動**,能以極低成本實現階層式勾選介面。掌握 Container.DataItemIndex 傳遞行索引、FindControl 取得內層控制項、DataKeys 保留主鍵,是 WebForms 開發的核心技能。