2008年7月23日 星期三

處理在 GridView 自定控制項觸發事件無法執行的問題

在GridView裡,常常會透過 ItemTemplate 放一些我們所需要的控制項。舉 DropDownList 為例,我們可能會在這個 DropDownList 裡放兩個值,A 與 B 。假設每次的選擇都會需要觸發 PostBack 以利我們做後續動作,所以除了將這個 DropDownList 的 AutoPostBack 設為 True 之外,還會在 .cs 程式裡寫 :



protected override void OnPreRender(EventArgs e)

{
if (!IsPostBack)
{
gv_coorgcd.DataSource = (DataTable)ViewState["tmpTable"];
gv_coorgcd.DataBind();
}
base.OnPreRender(e);
}

protected void gv_coorgcd_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
Control ctl=null;
ctl = e.Row.FindControl("ddl_orgtype");
if (ctl != null)
{
DropDownList ddl_orgtype = (DropDownList)ctl;
ddl_orgtype.SelectedIndexChanged += new
EventHandler(ddl_orgtype_SelectedIndexChanged);
}
}
}

protected void ddl_orgtype_SelectedIndexChanged(object sender, EventArgs e)
{
int rowindex = ((GridViewRow) (((DropDownList) sender ).NamingContainer )).RowIndex;
}

這程式裡隱含了很大的問題,但不容易被發現。從 覆載 OnPreRender( ) 來看,只有在第一次進入這個頁面時,才會去執行 gv (GrideView) 的資料繫結,並執行到 gv_coorgcd_RowDataBound(),也因此為 ddl_orgtype 註冊了 SelectedIndexChanged 事件。

然而,當 USER 觸發下拉清單 ddl_orgtype 觸發了 PostBack 後,這次並不會再執行 gv_coorgcd.DataBind(),也不會註冊 SelectedIndexChanged 事件,所以你會發現,ddl_orgtype_SelectedIndexChanged 根本就不再有機會被呼叫。

要解決這問題,目前有兩個方法,分別介紹一下:

方法一:
比較簡單直接。就是將註冊 ddl_orgtype 的 SelectedIndexChanged 事件,改由 .aspx 去寫,而不是原先的 .cs 。 所以,我們可以在 aspx 中寫入以下的程式:

<asp:GridView ID="gv_coorgcd" runat="server" AutoGenerateColumns="False"
OnRowDataBound="gv_coorgcd_RowDataBound">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:DropDownList ID="ddl_orgtype" runat="server" AutoPostBack="true"
OnTextChanged="ddl_orgtype_SelectedIndexChanged" >
<asp:ListItem Text="院內" Value="1" Selected="true"></asp:ListItem>
<asp:ListItem Text="院外" Value="2" ></asp:ListItem>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>

注意囉!就是 OnTextChanged="ddl_orgtype_SelectedIndexChanged" 。我們直接將這事件註冊在這個物件的預設宣告裡,就不用在 .cs 透過程式動態去新增了。

方法二:
雖然在第二次進入頁面時(IsPostBack)沒有再重新繫結 gv_coorgcd,但卻可以知道這次的 PostBack 是由誰所觸發。我們可以據此來決定後面所要執行的動作。

所以,可以在 OnPreRender( ) 事件加上下面的程式:
protected override void OnPreRender(EventArgs e)
{
if (!IsPostBack)
{
gv_coorgcd.DataSource = (DataTable)ViewState["tmpTable"];
gv_coorgcd.DataBind();
}
else
{
string post_target = Request.Form["__EVENTTARGET"];
if (post_target != null)
{
if (post_target.Contains("ddl_orgtype"))
{
//Do something about ddl_orgtype SelectedIndexChanged
}
}
}
base.OnPreRender(e);
}

透過 Request.Form["__EVENTTARGET"] 就可以取得觸發 PostBack 的始作俑者了。

當然,個人是偏好第一種方法,簡單實用又大方。

沒有留言:

張貼留言