[ASP.NET]DropDownList 奇妙的在OnChange沒有觸發下觸發了SelectedIndexChanged事件

[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

image

頁面

    <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事件被觸發  !!

image  image image image

 

可能的解答:http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.aspnet.webcontrols/2006-02/msg00147.html

image

 

Anson這個問題跟我原先遇到的比較像, 但也不完全相同. 但解法相同就是把ViewState啟用.

請容小弟也沒有能力再往下查了, 來去睡先.

 

Update: 後來 Anson 很強地弄出了一個很厲害的解答:http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.aspnet.webcontrols/2006-02/msg00149.html

太深奧了,都是沒用過的東西,待有心人士去試了。

image

 

專案下載點

 

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)