GridView 內建刪除僅支援單列。本文示範加入 CheckBox 列 (TemplateField),配合「刪除選取項目」按鈕,實現批次刪除功能。涵蓋分頁情境下跨頁選取狀態保持、全選/反選、批次執行與交易處理。
<asp:GridView ID="gvProducts" runat="server"
DataSourceID="odsProducts"
AutoGenerateColumns="False"
DataKeyNames="ProductID"
AllowPaging="True" PageSize="10"
OnRowDataBound="gvProducts_RowDataBound"
OnPreRender="gvProducts_PreRender">
<Columns>
<asp:TemplateField HeaderText="選取">
<HeaderTemplate>
<asp:CheckBox ID="cbSelectAll" runat="server"
onclick="toggleSelectAll(this)" />
</HeaderTemplate>
<ItemTemplate>
<asp:CheckBox ID="cbSelect" runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="ProductID" HeaderText="ID" ReadOnly="True" />
<asp:BoundField DataField="ProductName" HeaderText="產品名稱" />
<asp:BoundField DataField="UnitPrice" HeaderText="單價" DataFormatString="{0:C2}" HtmlEncode="False" />
<asp:BoundField DataField="UnitsInStock" HeaderText="庫存" />
<asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />
</Columns>
</asp:GridView>
<asp:Button ID="btnDeleteSelected" runat="server" Text="刪除選取項目"
OnClick="btnDeleteSelected_Click" OnClientClick="return confirm('確定刪除選取的產品?')" />
<asp:Label ID="lblStatus" runat="server" ForeColor="Green" />
<script type="text/javascript">
function toggleSelectAll(headerCheckbox) {
var grid = document.getElementById('<%= gvProducts.ClientID %>');
var checkboxes = grid.getElementsByTagName('input');
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].type == 'checkbox' && checkboxes[i].id != headerCheckbox.id) {
checkboxes[i].checked = headerCheckbox.checked;
}
}
}
</script>
private List SelectedProductIDs
{
get
{
if (ViewState["SelectedProductIDs"] == null)
ViewState["SelectedProductIDs"] = new List();
return (List)ViewState["SelectedProductIDs"];
}
set { ViewState["SelectedProductIDs"] = value; }
}
protected void gvProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
CheckBox cb = (CheckBox)e.Row.FindControl("cbSelect");
int productID = (int)gvProducts.DataKeys[e.Row.RowIndex].Value;
// 還原選取狀態
cb.Checked = SelectedProductIDs.Contains(productID);
// 附加 ProductID 以便客戶端/伺服端識別
cb.Attributes["data-product-id"] = productID.ToString();
}
}
protected void gvProducts_PreRender(object sender, EventArgs e)
{
// 頁面渲染前,根據目前頁面的 CheckBox 狀態更新 ViewState
foreach (GridViewRow row in gvProducts.Rows)
{
CheckBox cb = (CheckBox)row.FindControl("cbSelect");
int productID = (int)gvProducts.DataKeys[row.RowIndex].Value;
if (cb.Checked)
{
if (!SelectedProductIDs.Contains(productID))
SelectedProductIDs.Add(productID);
}
else
{
SelectedProductIDs.Remove(productID);
}
}
}
protected void btnDeleteSelected_Click(object sender, EventArgs e)
{
if (SelectedProductIDs.Count == 0)
{
lblStatus.Text = "請先選取要刪除的產品";
return;
}
int successCount = 0;
var bll = new BLL.ProductsBLL();
// 使用 TransactionScope 確保原子性 (需 System.Transactions 參考)
using (var scope = new System.Transactions.TransactionScope())
{
try
{
foreach (int id in SelectedProductIDs)
{
// 檢查業務規則:如有訂單明細不可刪除
if (bll.CanDeleteProduct(id))
{
bll.DeleteProduct(id);
successCount++;
}
}
scope.Complete();
}
catch (Exception ex)
{
lblStatus.Text = "刪除失敗:" + ex.Message;
return;
}
}
SelectedProductIDs.Clear();
gvProducts.DataBind();
lblStatus.Text = $"成功刪除 {successCount} 筆產品";
}
// 使用 TableAdapter 批次更新 (需 DAL 支援)
public void DeleteProductsBatch(List productIDs)
{
// 方案 A: 單一 SQL IN 子句 (效能最佳)
string ids = string.Join(",", productIDs);
string sql = $"DELETE FROM Products WHERE ProductID IN ({ids}) AND ProductID NOT IN (SELECT ProductID FROM Order_Details)";
_adapter.ExecuteNonQuery(sql);
// 方案 B: TableAdapter 批次更新 (需設定 BatchSize)
// _adapter.UpdateBatchSize = 100;
// foreach (id) _adapter.Delete(id);
// _adapter.Update(dataTable);
}
PreRender 存入 ViewStateRowDataBound 還原 ViewState 狀態 → 勾選 2 項btnDeleteSelected_Click 讀取 ViewState 共 5 個 ID → 批次刪除DataBind 重新整理使用 HiddenField 儲存 JSON 陣列,JavaScript 維護選取集合,Postback 時一次提交:
<asp:HiddenField ID="hfSelectedIDs" runat="server" />
<script>
var selected = [];
function toggleRow(cb, id) {
if (cb.checked) selected.push(id); else selected = selected.filter(x => x != id);
document.getElementById('<%= hfSelectedIDs.ClientID %>').value = JSON.stringify(selected);
}
// 全選邏輯類似
</script>
protected void btnDeleteSelected_Click(object sender, EventArgs e)
{
var ids = Newtonsoft.Json.JsonConvert.DeserializeObject>(hfSelectedIDs.Value);
// ... 批次刪除
hfSelectedIDs.Value = "[]";
}
GridView 批次操作核心在於:TemplateField CheckBox、跨頁狀態保持 (ViewState/HiddenField)、全選 JavaScript、交易包裝批次執行。此模式可擴充至批次編輯、批次匯出、批次狀態變更等情境。現代專案建議採用客戶端狀態管理 + Web API 批次端點,減少 ViewState 體積與 Postback 開銷。