DataList 控制項特色
DataList 是輕量級資料繫結控制項,支援自訂版面(Template)、內建 `ItemCommand` 事件處理按鈕命令、`DataKeyField` 存取主鍵值,適合簡單清單操作。
一、前端 .aspx 完整標記
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="DataListDelete.aspx.cs" Inherits="Demo.DataListDelete" %>
<!DOCTYPE html>
<html><head><title>DataList 刪除示範</title>
<script type="text/javascript">
// 全選/反選
function CheckAll(chk) {
var checkboxes = document.getElementsByTagName('input');
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].type === 'checkbox' && checkboxes[i].id !== chk.id) {
checkboxes[i].checked = chk.checked;
}
}
}
</script>
</head><body>
<form id="form1" runat="server">
<fieldset style="width:600px;">
<legend>DataList 刪除操作(支援批次刪除)</legend>
<asp:DataList ID="dlUsers" runat="server" DataKeyField="id" OnItemCommand="dlUsers_ItemCommand">
<HeaderTemplate>
<table border="1" cellpadding="0" cellspacing="0" style="width:100%; border-collapse:collapse;">
<tr style="background:#f0f0f0;">
<th width="50"><input type="checkbox" onclick="CheckAll(this)" title="全選/反選" /></th>
<th>編號</th><th>暱稱</th><th>簽名</th><th>操作</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td align="center"><asp:CheckBox ID="chkItem" runat="server" /></td>
<td><asp:Label ID="lblId" runat="server" Text='<%# Eval("id") %>' /></td>
<td><asp:Label ID="lblName" runat="server" Text='<%# Eval("bg_name") %>' /></td>
<td><asp:Label ID="lblSign" runat="server" Text='<%# Eval("bg_p_autograph") %>' /></td>
<td align="center">
<asp:Button ID="btnDel" runat="server" Text="刪除" CommandName="delete"
OnClientClick="return confirm('確定刪除此筆資料?');" CssClass="btn-del" />
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
<tr>
<td colspan="5" align="center">
<asp:Button ID="btnBatchDel" runat="server" Text="批次刪除選取" CommandName="batchdelete"
OnClientClick="return confirm('確定批次刪除選取項目?');" CssClass="btn-batch" />
</td>
</tr>
</table>
</FooterTemplate>
</asp:DataList>
</fieldset></form></body></html>
二、後端 .aspx.cs 完整邏輯
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.UI.WebControls;
namespace Demo
{
public partial class DataListDelete : System.Web.UI.Page
{
private readonly string _connStr = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) BindDataList();
}
private void BindDataList()
{
using (var conn = new SqlConnection(_connStr))
using (var da = new SqlDataAdapter("SELECT id, bg_name, bg_p_autograph FROM bg_spatial", conn))
{
var dt = new DataTable();
da.Fill(dt);
dlUsers.DataSource = dt;
dlUsers.DataBind();
}
}
protected void dlUsers_ItemCommand(object source, DataListCommandEventArgs e)
{
switch (e.CommandName)
{
case "delete": // 單筆刪除
DeleteSingle(e);
break;
case "batchdelete": // 批次刪除
DeleteBatch();
break;
}
}
private void DeleteSingle(DataListCommandEventArgs e)
{
int id = Convert.ToInt32(dlUsers.DataKeys[e.Item.ItemIndex]);
ExecuteDelete(id, "單筆刪除");
}
private void DeleteBatch()
{
int successCount = 0;
foreach (DataListItem item in dlUsers.Items)
{
if (item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem)
{
var chk = (CheckBox)item.FindControl("chkItem");
if (chk != null && chk.Checked)
{
int id = Convert.ToInt32(dlUsers.DataKeys[item.ItemIndex]);
if (ExecuteDelete(id, "批次刪除", false)) successCount++;
}
}
}
ShowMessage($"批次刪除完成,成功 {successCount} 筆。");
BindDataList();
}
private bool ExecuteDelete(int id, string actionName, bool showMsg = true)
{
try
{
using (var conn = new SqlConnection(_connStr))
using (var cmd = new SqlCommand("DELETE FROM bg_spatial WHERE id = @id", conn))
{
cmd.Parameters.AddWithValue("@id", id);
conn.Open();
int rows = cmd.ExecuteNonQuery();
if (showMsg) ShowMessage(rows > 0 ? $"{actionName} 成功!" : $"{actionName} 失敗,資料不存在。");
return rows > 0;
}
}
catch (Exception ex)
{
if (showMsg) ShowMessage($"{actionName} 發生錯誤:{ex.Message}");
return false;
}
}
private void ShowMessage(string msg)
{
ClientScript.RegisterStartupScript(GetType(), "msg", $"alert('{msg.Replace("'", "\\'")}');", true);
}
}
}
關鍵技術點解析
| 技術點 | 說明 |
| DataKeyField | 指定主鍵欄位 ``,避免在畫面顯示 ID 或藏在 HiddenField |
| DataKeys 集合 | `dlUsers.DataKeys[e.Item.ItemIndex]` 取得對應項目主鍵,型別安全 |
| CommandName | Button/LinkButton/ImageButton 設定 `CommandName`,統一在 `ItemCommand` 事件分派 |
| FindControl | `item.FindControl("chkItem")` 取得 Template 內控制項實例 |
| ItemType 判斷 | 只處理 `Item` / `AlternatingItem`,略過 Header/Footer/Separator |
| 參數化查詢 | `@id` 參數防止 SQL Injection,效能較好(計畫快取) |
| Using 區塊 | 確保連線/命令正確 Dispose,避免連線洩漏 |
進階優化建議
- 交易包裝批次刪除:多筆刪除建議用 `TransactionScope` 或 `SqlTransaction` 確保原子性。
- IN 子句單次刪除:收集所有 ID → `DELETE FROM table WHERE id IN (...)` 單次往返,效能優於迴圈。
- 軟刪除:加 `IsDeleted` 欄位,避免資料遺失,查詢加 `WHERE IsDeleted=0`。
- 前端增強:jQuery 統一綁定確認對話框、AJAX 非同步刪除、刪除後局部更新。
- 分頁/排序:DataList 原生不支援,需自行實作或改用 GridView/ListView。
常見錯誤與除錯
| 現象 | 原因 | 解決 |
| DataKeys 取值為 null/錯誤 | 未設定 DataKeyField 或 ItemIndex 錯誤 | 檢查 DataKeyField、確認 e.Item.ItemIndex |
| 批次刪除未勾選也刪除 | ItemType 未過濾 Header/Footer | 加 `if (item.ItemType == ListItemType.Item || ...)` |
| 刪除後畫面未更新 | 未重新 BindDataList() | 刪除成功後呼叫 BindDataList() |
| SQL 例外:外鍵衝突 | 關聯資料存在 | 先刪子表、或設定 CASCADE DELETE、或軟刪除 |