在 ASP.NET ListView 控制項的 ItemTemplate 中動態隱藏按鈕(如編輯、刪除)是常見需求。然而直接使用 FindControl 常導致「物件參考未設定為物件實例」錯誤。本文深入分析根因,提供兩種實務解決方案。
protected void ListView1_ItemCreated(object sender, ListViewItemEventArgs e)
{
Button DeleteButton = (Button)ListView1.FindControl("DeleteButton");
Button EditButton = (Button)ListView1.FindControl("EditButton");
string username = Session["username"].ToString();
if (username != "admin")
{
EditButton.Visible = false; // NullReferenceException
DeleteButton.Visible = false;
}
}
ListView 實作 INamingContainer 介面,會為每個資料列建立獨立的命名容器。執行時期,DeleteButton 實際 ID 會變為:
ListView1_ctrl0_DeleteButton
ListView1_ctrl1_DeleteButton
...
直接對 ListView 呼叫 FindControl 找不到具體資料列內的控制項,回傳 null,導致後續存取 Visible 屬性拋出例外。
protected void ListView1_ItemCreated(object sender, ListViewItemEventArgs e)
{
// 關鍵:使用 e.Item (當前資料列容器) 而非 ListView1
Button DeleteButton = (Button)e.Item.FindControl("DeleteButton");
Button EditButton = (Button)e.Item.FindControl("EditButton");
string username = Session["username"]?.ToString() ?? "";
// 防禦性檢查:確保控制項存在才操作
if (username != "admin" && DeleteButton != null && EditButton != null)
{
EditButton.Visible = false;
DeleteButton.Visible = false;
}
}
e.Item.FindControl:ListViewItemEventArgs.Item 代表當前資料列的命名容器?. 和 ?? 避免 Session 為 null 時拋出例外<asp:Button ID="DeleteButton" runat="server"
CommandName="Delete" Text="刪除"
Visible='<%# Session["username"] != null && Session["username"].ToString() == "admin" %>' />
<asp:Button ID="EditButton" runat="server"
CommandName="Edit" Text="編輯"
Visible='<%# Session["username"] != null && Session["username"].ToString() == "admin" %>' />
DataBind() 或控制項需在繫結容器中<%# CanEdit() %><!-- 後端屬性 -->
public bool CanDelete
{
get { return User.IsInRole("Admin") || User.Identity.Name == "admin"; }
}
<-- 標記 -->
<asp:Button ID="DeleteButton" runat="server"
Visible='<%# Container.DataItem != null && CanDelete %>' />
| 維度 | 方案一:ItemCreated + FindControl | 方案二:宣告式 Visible 繫結 |
|---|---|---|
| 程式碼位置 | 後端 .cs | 前端 .aspx |
| 複雜邏輯支援 | 強(完整 C#) | 中(運算式/屬性呼叫) |
| 效能 | 需遍歷控制項樹 | 繫結時直接評估 |
| 維護性 | 邏輯分散 | 邏輯集中於模板 |
| 適用場景 | 動態條件、需存取資料項屬性 | 靜態/半靜態權限、角色控制 |
ListView 隱藏按鈕的核心在於理解 INamingContainer 機制與 資料繫結生命週期。方案二(宣告式繫結)因簡潔、高效、易維護,應作為首選;方案一適用於需在伺服端執行複雜邏輯判斷的場景。實務建議:優先使用宣告式 Visible 繫結,複雜情況再考慮後端控制。