[ASP.NET]DropDownList 奇妙的在Page沒有變更下觸發SelectedIndexChanged事件
我的環境
1. ASP.NET 3.5
2. Visual Studio 2008
問題:
1. 我有一個 GridView 未啟用 ViewState, 但它的資料集 ObjectDataSource 有啟動 ViewState.
2. GridView 裡頭放了一個欄位為動態產生之DropDownList.
3. 當 GridView databind 時會不預期的觸發 SelectedIndexChanged.
釐清問題:
第一步:是誰觸發?(Client or Server)
作法:加入 jQuery.change事件 + IE9 develop tool 中斷點. 確定Client 並沒有觸發OnChange事件
第二步:在什麼狀況被觸發?
作法:交叉測試各種可能,並重建問題流程。
第三步:如果有Open Source可以去看Code, 如果沒有只能先問問G大神, 或是各種參數調一調動一動?
作法:在很艱難的測試下,發現GridView 未啟用 ViewState時才會出現。
重建問題:(PS. 很可惜我做了簡單的頁面後, 行為結果與之前遇到的不同, 但仍有不預期的觸發 SelectedIndexChanged)
簡單的業務邏輯:
1.
/// <summary>
/// Saved the student information
/// </summary>
public class StudentEntity
{
public string StudentName { get; set; }
public int Number { get; set; }
public string ClassName { get; set; }
}
2.
/// <summary>
/// The sample data contains student's information.
/// </summary>
public class StudentScription: List<StudentEntity>
{
public static StudentScription Students = new StudentScription();
private StudentScription()
{
this.Add(new StudentEntity() { StudentName = "Flora Chen", Number = 1, ClassName = "Senior" });
this.Add(new StudentEntity() { StudentName = "Elsa Li", Number = 2, ClassName = "Senior" });
this.Add(new StudentEntity() { StudentName = "Ann Wang", Number = 3, ClassName = "Senior" });
this.Add(new StudentEntity() { StudentName = "Mark Hu", Number = 1, ClassName = "Junior" });
this.Add(new StudentEntity() { StudentName = "Jay Lin", Number = 2, ClassName = "Junior" });
}
}
3.
public class DataAccessObject
{
/// <summary>
/// Returns the students by class name.
/// Ps: This data for gridview to fill it's cells.
/// </summary>
/// <param name="ClassName"></param>
/// <returns></returns>
public IEnumerable<StudentEntity> GetStudentsByClassName(string ClassName)
{
var v = from _v in StudentScription.Students
where _v.ClassName.Equals(ClassName, StringComparison.OrdinalIgnoreCase)
select _v;
return v;
}
/// <summary>
/// Returns the number list by class name.
/// Ps: This data for dropdownlist to fill it's list items.
/// </summary>
/// <param name="ClassName"></param>
/// <returns></returns>
public IEnumerable<int> GetNumberListByClassName(string ClassName)
{
var v = from _v in GetStudentsByClassName(ClassName)
select _v.Number;
return v;
}
}
頁面邏輯
1. 點班別名稱 -> 2. 顯示該班別學生 -> 3. 當選擇Number時, 會顯Message
頁面
<div>
<div><asp:LinkButton ID="ClassNameSenior" runat="server" OnClick="ClassName_Click" Text="Senior"></asp:LinkButton></div>
<div><asp:LinkButton ID="ClassNameJunior" runat="server" OnClick="ClassName_Click" Text="Junior"></asp:LinkButton></div>
<asp:GridView ID="Students" runat="server" EnableViewState="False" DataSourceID="ObjectDataSource1"
OnRowDataBound="Students_RowsDataBound" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="ClassName" HeaderText="Class Name" />
<asp:BoundField DataField="StudentName" HeaderText="Name" />
<asp:TemplateField HeaderText="Number">
<ItemTemplate>
<asp:DropDownList ID="Number" runat="server" DataFieldValue='<%# Eval("Number") %>' ClassName='<%# Eval("ClassName") %>' OnSelectedIndexChanged="Number_SelectedIndexChanged" AutoPostBack="true"></asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" EnableViewState="true"
SelectMethod="GetStudentsByClassName" TypeName="ForDemo.DataAccessObject">
<SelectParameters>
<asp:Parameter Name="ClassName" Type="String" />
</SelectParameters>
</asp:ObjectDataSource>
Message:<asp:Label ID="Message" runat="server"></asp:Label>
</div>
頁面程式
/// <summary>
/// When page load, clear message's text.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{
Message.Text = string.Empty;
}
/// <summary>
/// When class name clicked, it'll bind the student's data to gridview.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void ClassName_Click(object sender, EventArgs e)
{
LinkButton Link = sender as LinkButton;
ObjectDataSource1.SelectParameters[0].DefaultValue = Link.Text;
Students.DataBind();
}
/// <summary>
/// When girdview data bound, it'll bind the number to dropdownlist and set the selected value.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Students_RowsDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DropDownList Number = e.Row.FindControl("Number") as DropDownList;
if (Number != null)
{
string ClassName = Number.Attributes["ClassName"];
IEnumerable<int> Numbers = new DataAccessObject().GetNumberListByClassName(ClassName);
foreach (int i in Numbers)
{
Number.Items.Add(i.ToString());
}
Number.SelectedValue = Number.Attributes["DataFieldValue"];
}
}
}
/// <summary>
/// When number selected index changed, set the selected value to message text.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Number_SelectedIndexChanged(object sender, EventArgs e)
{
DropDownList Number = sender as DropDownList;
Message.Text = "You selected number - " + Number.SelectedValue;
}
錯誤的過程
1. 點班別名稱
2. 選擇座號(Number) <- 觸發OnChange 及 SelectedIndexChanged事件
3. 點另一個班別名稱 <- !! SelectedIndexChanged事件被觸發 !!
Anson這個問題跟我原先遇到的比較像, 但也不完全相同. 但解法相同就是把ViewState啟用.
請容小弟也沒有能力再往下查了, 來去睡先.
Update: 後來 Anson 很強地弄出了一個很厲害的解答:http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.aspnet.webcontrols/2006-02/msg00149.html
太深奧了,都是沒用過的東西,待有心人士去試了。
ps. 謝謝 91 哥的熱情協助.
待讀: http://msdn.microsoft.com/zh-tw/magazine/cc163901(en-us).aspx (Speed Up Your Site with the Improved View State in ASP.NET 2.0)