[ASP.NET]如何透過動態新增的控制項,再去動態新增控制項
前言
這是一篇MSDN forum上,發問人提出的疑問,請參考:asp.net 為什麼不能兩個按鈕事件?。
其實在ASP.NET webform上,要透過server端的code來動態產生控制項,是一件吃力的事。因為http是無狀態的,postback後要記住『值』,ASP.NET幾乎都是仰賴ViewState。雖然不是做不到,但是當動態新增控制項完之後,如果還要與UI上其他東西互動,或是需求異動後,某一個動態新增的控制項又要額外做某些功能,不管是在開發、維護或是偵錯上,這都是一件相當吃力的事。
除非,這樣的動態新增控制項滿足幾個基本要件,不然我自己還是偏向避開太動態的設計。
- 只有呈現資料,沒有互動的潛藏需求。
- 最多兩層的動態產生,不然程式碼會變得相當冗長跟硬幹。
- 動態新增的部分是user control,因為邏輯都被封裝在裡面了,就像widget, webpart, igoogle,每一塊都可以獨立作業。
雖然我不是挺喜歡這樣的設計方式,但是既然在Forum上已經不只一個人問過了,我就隨手把sample code做個記錄,方便真的避不開這需求的人可以快速的參考一下。
Sample Code
.aspx
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel ID="Panel1" runat="server">
<asp:Label ID="Label1" runat="server" Text="Button1 Group"></asp:Label>
</asp:Panel>
<asp:Panel ID="Panel2" runat="server">
<asp:Label ID="Label2" runat="server" Text="Button2 Group"></asp:Label>
</asp:Panel>
<asp:Label ID="result" runat="server" Text="result"></asp:Label>
</div>
</form>
</body>
</html>
.aspx.cs
public partial class DynamicAddingCOntrols : System.Web.UI.Page
{
/// <summary>
/// 用來判斷是否為第二群組的按鈕觸發的postback,如果是,記得也要再generate一次第二群組的按鈕,只要出現過一次,之後的postback理論上都要再出現
/// </summary>
private bool Myflag
{
get
{
if (this.ViewState["Myflag"] == null)
{
this.ViewState["Myflag"] = false;
}
return Convert.ToBoolean(this.ViewState["Myflag"]);
}
set
{
this.ViewState["Myflag"] = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
List<string> firstButtonClientIdCollection = new List<string>();
for (int i = 0; i < 3; i++)
{
Button button = new Button();
button.ID = "Button1_" + i.ToString();
firstButtonClientIdCollection.Add(button.ClientID);
button.UseSubmitBehavior = false;
button.Text = button.ID;
button.Click += new EventHandler(button_Click);
this.Panel1.Controls.Add(button);
}
if (IsPostBack)
{
//代表是first button group觸發的postback
if (firstButtonClientIdCollection.IndexOf(Convert.ToString(this.Request.Form["__EVENTTARGET"])) != -1 || this.Myflag)
{
this.GenerateSecondButtonGroup();
}
}
}
/// <summary>
/// 產生第二組button group
/// </summary>
private void GenerateSecondButtonGroup()
{
this.Myflag = true;
for (int i = 0; i < 3; i++)
{
Button button = new Button();
button.ID = "Button2_" + i.ToString();
button.Text = button.ID;
button.Click += new EventHandler(button_Click);
this.Panel2.Controls.Add(button);
}
}
/// <summary>
/// button的event handler,紀錄是哪一顆按鈕觸發的postback
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void button_Click(object sender, EventArgs e)
{
this.result.Text = string.Format("觸發的ButtonID:{0}", (sender as Button).ID);
}
}
結論
幾個重點提醒:
- 動態新增控制項,因為http無狀態,所以每一次的page life cycle都應該要做Controls.Add的動作,至於上面的值如何在postback之間記住,那就是控制項本身透過ViewState會去記值了,developer就不用擔心這問題,只要位置放對就可以。
- 怎麼讀動態新增控制項上面的值?基本上只能透過FindControl或是Request.Form[]去讀,所以ID的命名通常都要按照一定的rule。
- 以這篇文章有兩層動態新增控制項為例,很多東西是得透過Request.Form[“__EVENTTARGET”]去攔截判斷是什麼東西觸發的postback,再轉發(呼叫)要處理的事情。很多東西寫在對應的control event,會因為page life cycle的事件順序而導致潛藏的bug。
- 必要時可以用ViewState來記住一些size比較小的值,例如我們的event handler中,需要記住某些東西給下一次用的情況。
這篇範例只是一些概念上的組合和呈現,相信我,實際上會碰到的需求都沒這麼簡單,而且需求很容易發散,屆時就會收不了網,被質疑為什麼這樣的功能這麼難做。(尤其是你的PM, SA不懂Web的時候)
blog 與課程更新內容,請前往新站位置:http://tdd.best/