Home All Groups Group Topic Archive Search About

ASP.NET 2.0 Dropdownlist EnableViewState=false SelectedIndexChange

Author
9 Feb 2006 5:19 PM
Anson Goldade
I'm trying to leverage the viewstate optimization capabilities in ASP.NET 2.0
but have run into an issue with the DropDownList. 

Problem statement
SelectedIndexChanged events are getting fired for every dropdown since the
page was first rendered.  Here's a sequence example.
1. Load page
2. Change value in ddl #1 ... causes postback.  SelectedIndexChanged fired
for ddl #1.
3. Change value in ddl #2 ... causes postback.  SelectedIndexChanged fired
for ddl #1 and ddl #2.  This is the unexpected behavior.  Would only expect
SelectedIndexChanged to be fired for ddl #2

Assumptions
1. Multiple drop down lists on page
2. Autopostback = true
3. EnableViewState on page = false
4. Each ddl has it's own selectedindexchanged event
5. Lists in the ddl's are being rebuilt by overriding OnPreInit on page. 
Also tried populating lists in OnInit and OnInitComplete ... same result
6. Subscribe to SelectedIndexChanged event in OnLoad.  Also tried setting
onselectedindexchanged properties of ddl in aspx ... same result.

I'd like to know what I need to do in order to get only the
SelectedIndexChanged event to fire for the control that actually changed.  If
I set EnableViewState=true, the expected behavior is exhibited.  I've
attached a sample scenario below.  Your help is appreciated.  Thanks.

Anson

<aspx_page>

<%@ Page Language="C#" AutoEventWireup="false"
CodeFile="TestMultipleEventsOnPostback.aspx.cs"
Inherits="TestMultipleEventsOnPostback" enableviewstate="false" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>DropDownList SelectedIndexChanged EnableViewState="False"
Example</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:label id="summary" runat="server"></asp:label>
        <br />
        <br />
        <table width="100%">
            <tr>
                <td>List #1</td>
                <td>List #2</td>
                <td>Repeater</td>
            </tr>
            <tr>
                <td><asp:dropdownlist id="list1" runat="server"
autopostback="true"></asp:dropdownlist></td>
                <td><asp:dropdownlist id="list2" runat="server"
autopostback="true"></asp:dropdownlist></td>
                <td>
                    <table>
                    <asp:repeater id="repeat" runat="server">
                        <itemtemplate>
                            <tr>
                                <td><asp:label id="itemTemplateLabel"
runat="server"></asp:label></td>
                                <td><asp:dropdownlist id="itemTemplateList"
runat="server" autopostback="true"></asp:dropdownlist></td>
                            </tr>
                        </itemtemplate>
                    </asp:repeater>
                    </table>
                </td>
            </tr>
        </table>

    </div>
    </form>
</body>
</html>

</aspx_page>

<aspx_cs_codebehind>

using System;
using System.Collections.Generic;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class TestMultipleEventsOnPostback : System.Web.UI.Page
{
    //items for list #1
    private Dictionary<string, string> _list1Items = new Dictionary<string,
string>();
    //items for list #2
    private Dictionary<string, string> _list2Items = new Dictionary<string,
string>();
    //items for repeater drop down
    private Dictionary<string, string> _repeaterListItems = new
Dictionary<string, string>();
    //repeater source just to get 5 items created
    private object[] _repeaterSource = new object[5];
    public TestMultipleEventsOnPostback()
    {
        //create the list 1 items
        for (int i = 0; i <= 10; i++)
        {
            this._list1Items.Add(i.ToString(), string.Format("Value = {0}",
i));
        }
        //create the list 2 items
        for (int i = 0; i <= 40; i++)
        {
            this._list2Items.Add(i.ToString(), string.Format("Value = {0}",
i));
        }
        //create the list repeater items
        for (int i = 0; i <= 60; i++)
        {
            this._repeaterListItems.Add(i.ToString(), string.Format("Value =
{0}", i));
        }
    }
    protected override void OnPreInit(EventArgs e)
    {
        //populate the drop down lists and rebuild the repeater
        this.list1.DataTextField = "Value";
        this.list1.DataValueField = "Key";
        this.list1.DataSource = this._list1Items;
        this.list1.DataBind();

        this.list2.DataTextField = "Value";
        this.list2.DataValueField = "Key";
        this.list2.DataSource = this._list2Items;
        this.list2.DataBind();

        //rebuild the repeater control
        this.repeat.ItemDataBound += new
RepeaterItemEventHandler(repeat_ItemDataBound);
        this.repeat.DataSource = this._repeaterSource;
        this.repeat.DataBind();

        base.OnPreInit(e);
    }
    protected override void OnLoad(EventArgs e)
    {
        if (!this.IsPostBack)
        {
            this.summary.Text = "No postback";
        }
        else
        {
            //clear out the summary label
            this.summary.Text = "<b>Event's that fired for current postback
(Note: I would only expect 1 because all the dropdowns have
autopostback=\"true\")</b>";
        }
        //set up the event handlers
        this.list1.SelectedIndexChanged += new
EventHandler(list1_SelectedIndexChanged);
        this.list2.SelectedIndexChanged += new
EventHandler(list2_SelectedIndexChanged);
        //set up the event for the drop down in the repeater
        foreach (RepeaterItem item in this.repeat.Items)
        {
            DropDownList ddl =
(DropDownList)item.FindControl("itemTemplateList");
            ddl.SelectedIndexChanged += new
EventHandler(itemTemplateList_SelectedIndexChanged);
        }
        base.OnLoad(e);
    }

    protected void itemTemplateList_SelectedIndexChanged(object sender,
EventArgs e)
    {
        this.summary.Text += "<br/>itemTemplateList_SelectedIndexChanged";
    }

    protected void list2_SelectedIndexChanged(object sender, EventArgs e)
    {
        this.summary.Text += "<br/>list2_SelectedIndexChanged";
    }

    protected void list1_SelectedIndexChanged(object sender, EventArgs e)
    {
        this.summary.Text += "<br/>list1_SelectedIndexChanged";
    }

    protected void repeat_ItemDataBound(object sender, RepeaterItemEventArgs
e)
    {
        DropDownList ddl =
(DropDownList)e.Item.FindControl("itemTemplateList");
        ddl.DataTextField = "Value";
        ddl.DataValueField = "Key";
        ddl.DataSource = this._repeaterListItems;
        ddl.DataBind();
    }
}

</aspx_cs_codebehind>

Author
9 Feb 2006 5:55 PM
Phillip Williams
Hi Anson,

I put your code on my site to illustrate to you what is happening:
http://www.webswapp.com/codesamples/testMultipleEventsOnPostBack.aspx

The problem here is that when you set the EnableViewState to false, you have
reconstructed the dropdownlists on every postback.  After reconstructing
them, (at the end of the OnPreInit), the selectedindex is always =0.  
Therefore if you had changed the first dropdownlist (which fired the first
event) then changed the second dropdownlist, the event of the first would
still be fired because as far as the page is concerned you have changed both
dropdownlist from the time you reconstructed them.

Your problem disappeared when you set the EnableViewState to true because
after the OnPreInit the state of the first dropdownlist (from previous
postback) changed the selectedIndex of your list and therefore the
dropdownlist did not sense a change in the selectedindex.

Show quoteHide quote
"Anson Goldade" wrote:

> I'm trying to leverage the viewstate optimization capabilities in ASP.NET 2.0
> but have run into an issue with the DropDownList. 
>
> Problem statement
> SelectedIndexChanged events are getting fired for every dropdown since the
> page was first rendered.  Here's a sequence example.
> 1. Load page
> 2. Change value in ddl #1 ... causes postback.  SelectedIndexChanged fired
> for ddl #1.
> 3. Change value in ddl #2 ... causes postback.  SelectedIndexChanged fired
> for ddl #1 and ddl #2.  This is the unexpected behavior.  Would only expect
> SelectedIndexChanged to be fired for ddl #2
>
> Assumptions
> 1. Multiple drop down lists on page
> 2. Autopostback = true
> 3. EnableViewState on page = false
> 4. Each ddl has it's own selectedindexchanged event
> 5. Lists in the ddl's are being rebuilt by overriding OnPreInit on page. 
> Also tried populating lists in OnInit and OnInitComplete ... same result
> 6. Subscribe to SelectedIndexChanged event in OnLoad.  Also tried setting
> onselectedindexchanged properties of ddl in aspx ... same result.
>
> I'd like to know what I need to do in order to get only the
> SelectedIndexChanged event to fire for the control that actually changed.  If
> I set EnableViewState=true, the expected behavior is exhibited.  I've
> attached a sample scenario below.  Your help is appreciated.  Thanks.
>
> Anson
>
> <aspx_page>
>
> <%@ Page Language="C#" AutoEventWireup="false"
> CodeFile="TestMultipleEventsOnPostback.aspx.cs"
> Inherits="TestMultipleEventsOnPostback" enableviewstate="false" %>
>
> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
>
> <html xmlns="http://www.w3.org/1999/xhtml" >
> <head runat="server">
>     <title>DropDownList SelectedIndexChanged EnableViewState="False"
> Example</title>
> </head>
> <body>
>     <form id="form1" runat="server">
>     <div>
>         <asp:label id="summary" runat="server"></asp:label>
>         <br />
>         <br />
>         <table width="100%">
>             <tr>
>                 <td>List #1</td>
>                 <td>List #2</td>
>                 <td>Repeater</td>
>             </tr>
>             <tr>
>                 <td><asp:dropdownlist id="list1" runat="server"
> autopostback="true"></asp:dropdownlist></td>
>                 <td><asp:dropdownlist id="list2" runat="server"
> autopostback="true"></asp:dropdownlist></td>
>                 <td>
>                     <table>
>                     <asp:repeater id="repeat" runat="server">
>                         <itemtemplate>
>                             <tr>
>                                 <td><asp:label id="itemTemplateLabel"
> runat="server"></asp:label></td>
>                                 <td><asp:dropdownlist id="itemTemplateList"
> runat="server" autopostback="true"></asp:dropdownlist></td>
>                             </tr>
>                         </itemtemplate>
>                     </asp:repeater>
>                     </table>
>                 </td>
>             </tr>
>         </table>
>        
>     </div>
>     </form>
> </body>
> </html>
>
> </aspx_page>
>
> <aspx_cs_codebehind>
>
> using System;
> using System.Collections.Generic;
> using System.Data;
> using System.Configuration;
> using System.Collections;
> using System.Web;
> using System.Web.Security;
> using System.Web.UI;
> using System.Web.UI.WebControls;
> using System.Web.UI.WebControls.WebParts;
> using System.Web.UI.HtmlControls;
>
> public partial class TestMultipleEventsOnPostback : System.Web.UI.Page
> {
>     //items for list #1
>     private Dictionary<string, string> _list1Items = new Dictionary<string,
> string>();
>     //items for list #2
>     private Dictionary<string, string> _list2Items = new Dictionary<string,
> string>();
>     //items for repeater drop down
>     private Dictionary<string, string> _repeaterListItems = new
> Dictionary<string, string>();
>     //repeater source just to get 5 items created
>     private object[] _repeaterSource = new object[5];
>     public TestMultipleEventsOnPostback()
>     {
>         //create the list 1 items
>         for (int i = 0; i <= 10; i++)
>         {
>             this._list1Items.Add(i.ToString(), string.Format("Value = {0}",
> i));
>         }
>         //create the list 2 items
>         for (int i = 0; i <= 40; i++)
>         {
>             this._list2Items.Add(i.ToString(), string.Format("Value = {0}",
> i));
>         }
>         //create the list repeater items
>         for (int i = 0; i <= 60; i++)
>         {
>             this._repeaterListItems.Add(i.ToString(), string.Format("Value =
> {0}", i));
>         }
>     }
>     protected override void OnPreInit(EventArgs e)
>     {
>         //populate the drop down lists and rebuild the repeater
>         this.list1.DataTextField = "Value";
>         this.list1.DataValueField = "Key";
>         this.list1.DataSource = this._list1Items;
>         this.list1.DataBind();
>
>         this.list2.DataTextField = "Value";
>         this.list2.DataValueField = "Key";
>         this.list2.DataSource = this._list2Items;
>         this.list2.DataBind();
>
>         //rebuild the repeater control
>         this.repeat.ItemDataBound += new
> RepeaterItemEventHandler(repeat_ItemDataBound);
>         this.repeat.DataSource = this._repeaterSource;
>         this.repeat.DataBind();
>
>         base.OnPreInit(e);
>     }
>     protected override void OnLoad(EventArgs e)
>     {
>         if (!this.IsPostBack)
>         {
>             this.summary.Text = "No postback";
>         }
>         else
>         {
>             //clear out the summary label
>             this.summary.Text = "<b>Event's that fired for current postback
> (Note: I would only expect 1 because all the dropdowns have
> autopostback=\"true\")</b>";
>         }
>         //set up the event handlers
>         this.list1.SelectedIndexChanged += new
> EventHandler(list1_SelectedIndexChanged);
>         this.list2.SelectedIndexChanged += new
> EventHandler(list2_SelectedIndexChanged);
>         //set up the event for the drop down in the repeater
>         foreach (RepeaterItem item in this.repeat.Items)
>         {
>             DropDownList ddl =
> (DropDownList)item.FindControl("itemTemplateList");
>             ddl.SelectedIndexChanged += new
> EventHandler(itemTemplateList_SelectedIndexChanged);
>         }
>         base.OnLoad(e);
>     }
>
>     protected void itemTemplateList_SelectedIndexChanged(object sender,
> EventArgs e)
>     {
>         this.summary.Text += "<br/>itemTemplateList_SelectedIndexChanged";
>     }
>
>     protected void list2_SelectedIndexChanged(object sender, EventArgs e)
>     {
>         this.summary.Text += "<br/>list2_SelectedIndexChanged";
>     }
>
>     protected void list1_SelectedIndexChanged(object sender, EventArgs e)
>     {
>         this.summary.Text += "<br/>list1_SelectedIndexChanged";
>     }
>
>     protected void repeat_ItemDataBound(object sender, RepeaterItemEventArgs
> e)
>     {
>         DropDownList ddl =
> (DropDownList)e.Item.FindControl("itemTemplateList");
>         ddl.DataTextField = "Value";
>         ddl.DataValueField = "Key";
>         ddl.DataSource = this._repeaterListItems;
>         ddl.DataBind();
>     }
> }
>
> </aspx_cs_codebehind>
>
Author
9 Feb 2006 6:50 PM
Anson Goldade
Phillip,

I absolutely agree with our assessment.  However, with the new ControlState
model that was implemented in controls in ASP.NET 2.0, we shouldn't need view
state to have the control update it's selectedindex between when the control
is populated in PreInit and when the event is subscribed to in OnLoad. 
According to the MSDN docs, all viewstate (and I'm assuming that includes
controlstate) is restored by the time that OnLoad is called.  This should
mean that the selected index has been reset to the value it was prior to the
postback. 

To illustrate this, disable ViewState and check what the selected index just
prior to me subscribing to the selectedindexchanged events in OnLoad.  Since
I don't subscribe to the events until the index has been reset (and prior to
the postback data being processed) only the controls that actually had value
changes should fire the selectedindexchanged events.

Perhaps I'm missing something with the new ControlState model.  I started
going down this path based on the following article.
http://msdn.microsoft.com/msdnmag/issues/04/10/ViewState/default.aspx

The overall goal here is to not have to enable viewstate, but continue to
have the controls firing the server side events.  I don't mind repopulating
the list controls ... in fact I find that much preferrable to serializing 10K
of viewstate to a client that just ends up posting that back to me.

I'm just trying to figure out if I'm doing something wrong or if I'm missing
the point completely.  Thanks again for your time and feedback and I look
forward to working with you toward a resolution.

Anson Goldade

Show quoteHide quote
"Phillip Williams" wrote:

> Hi Anson,
>
> I put your code on my site to illustrate to you what is happening:
> http://www.webswapp.com/codesamples/testMultipleEventsOnPostBack.aspx
>
> The problem here is that when you set the EnableViewState to false, you have
> reconstructed the dropdownlists on every postback.  After reconstructing
> them, (at the end of the OnPreInit), the selectedindex is always =0.  
> Therefore if you had changed the first dropdownlist (which fired the first
> event) then changed the second dropdownlist, the event of the first would
> still be fired because as far as the page is concerned you have changed both
> dropdownlist from the time you reconstructed them.
>
> Your problem disappeared when you set the EnableViewState to true because
> after the OnPreInit the state of the first dropdownlist (from previous
> postback) changed the selectedIndex of your list and therefore the
> dropdownlist did not sense a change in the selectedindex.
>
> --
> HTH,
> Phillip Williams
> http://www.societopia.net
> http://www.webswapp.com
>
>
> "Anson Goldade" wrote:
>
> > I'm trying to leverage the viewstate optimization capabilities in ASP.NET 2.0
> > but have run into an issue with the DropDownList. 
> >
> > Problem statement
> > SelectedIndexChanged events are getting fired for every dropdown since the
> > page was first rendered.  Here's a sequence example.
> > 1. Load page
> > 2. Change value in ddl #1 ... causes postback.  SelectedIndexChanged fired
> > for ddl #1.
> > 3. Change value in ddl #2 ... causes postback.  SelectedIndexChanged fired
> > for ddl #1 and ddl #2.  This is the unexpected behavior.  Would only expect
> > SelectedIndexChanged to be fired for ddl #2
> >
> > Assumptions
> > 1. Multiple drop down lists on page
> > 2. Autopostback = true
> > 3. EnableViewState on page = false
> > 4. Each ddl has it's own selectedindexchanged event
> > 5. Lists in the ddl's are being rebuilt by overriding OnPreInit on page. 
> > Also tried populating lists in OnInit and OnInitComplete ... same result
> > 6. Subscribe to SelectedIndexChanged event in OnLoad.  Also tried setting
> > onselectedindexchanged properties of ddl in aspx ... same result.
> >
> > I'd like to know what I need to do in order to get only the
> > SelectedIndexChanged event to fire for the control that actually changed.  If
> > I set EnableViewState=true, the expected behavior is exhibited.  I've
> > attached a sample scenario below.  Your help is appreciated.  Thanks.
> >
> > Anson
> >
> > <aspx_page>
> >
> > <%@ Page Language="C#" AutoEventWireup="false"
> > CodeFile="TestMultipleEventsOnPostback.aspx.cs"
> > Inherits="TestMultipleEventsOnPostback" enableviewstate="false" %>
> >
> > <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
> > "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
> >
> > <html xmlns="http://www.w3.org/1999/xhtml" >
> > <head runat="server">
> >     <title>DropDownList SelectedIndexChanged EnableViewState="False"
> > Example</title>
> > </head>
> > <body>
> >     <form id="form1" runat="server">
> >     <div>
> >         <asp:label id="summary" runat="server"></asp:label>
> >         <br />
> >         <br />
> >         <table width="100%">
> >             <tr>
> >                 <td>List #1</td>
> >                 <td>List #2</td>
> >                 <td>Repeater</td>
> >             </tr>
> >             <tr>
> >                 <td><asp:dropdownlist id="list1" runat="server"
> > autopostback="true"></asp:dropdownlist></td>
> >                 <td><asp:dropdownlist id="list2" runat="server"
> > autopostback="true"></asp:dropdownlist></td>
> >                 <td>
> >                     <table>
> >                     <asp:repeater id="repeat" runat="server">
> >                         <itemtemplate>
> >                             <tr>
> >                                 <td><asp:label id="itemTemplateLabel"
> > runat="server"></asp:label></td>
> >                                 <td><asp:dropdownlist id="itemTemplateList"
> > runat="server" autopostback="true"></asp:dropdownlist></td>
> >                             </tr>
> >                         </itemtemplate>
> >                     </asp:repeater>
> >                     </table>
> >                 </td>
> >             </tr>
> >         </table>
> >        
> >     </div>
> >     </form>
> > </body>
> > </html>
> >
> > </aspx_page>
> >
> > <aspx_cs_codebehind>
> >
> > using System;
> > using System.Collections.Generic;
> > using System.Data;
> > using System.Configuration;
> > using System.Collections;
> > using System.Web;
> > using System.Web.Security;
> > using System.Web.UI;
> > using System.Web.UI.WebControls;
> > using System.Web.UI.WebControls.WebParts;
> > using System.Web.UI.HtmlControls;
> >
> > public partial class TestMultipleEventsOnPostback : System.Web.UI.Page
> > {
> >     //items for list #1
> >     private Dictionary<string, string> _list1Items = new Dictionary<string,
> > string>();
> >     //items for list #2
> >     private Dictionary<string, string> _list2Items = new Dictionary<string,
> > string>();
> >     //items for repeater drop down
> >     private Dictionary<string, string> _repeaterListItems = new
> > Dictionary<string, string>();
> >     //repeater source just to get 5 items created
> >     private object[] _repeaterSource = new object[5];
> >     public TestMultipleEventsOnPostback()
> >     {
> >         //create the list 1 items
> >         for (int i = 0; i <= 10; i++)
> >         {
> >             this._list1Items.Add(i.ToString(), string.Format("Value = {0}",
> > i));
> >         }
> >         //create the list 2 items
> >         for (int i = 0; i <= 40; i++)
> >         {
> >             this._list2Items.Add(i.ToString(), string.Format("Value = {0}",
> > i));
> >         }
> >         //create the list repeater items
> >         for (int i = 0; i <= 60; i++)
> >         {
> >             this._repeaterListItems.Add(i.ToString(), string.Format("Value =
> > {0}", i));
> >         }
> >     }
> >     protected override void OnPreInit(EventArgs e)
> >     {
> >         //populate the drop down lists and rebuild the repeater
> >         this.list1.DataTextField = "Value";
> >         this.list1.DataValueField = "Key";
> >         this.list1.DataSource = this._list1Items;
> >         this.list1.DataBind();
> >
> >         this.list2.DataTextField = "Value";
> >         this.list2.DataValueField = "Key";
> >         this.list2.DataSource = this._list2Items;
> >         this.list2.DataBind();
> >
> >         //rebuild the repeater control
> >         this.repeat.ItemDataBound += new
> > RepeaterItemEventHandler(repeat_ItemDataBound);
> >         this.repeat.DataSource = this._repeaterSource;
> >         this.repeat.DataBind();
> >
> >         base.OnPreInit(e);
> >     }
> >     protected override void OnLoad(EventArgs e)
> >     {
> >         if (!this.IsPostBack)
> >         {
> >             this.summary.Text = "No postback";
> >         }
> >         else
> >         {
> >             //clear out the summary label
> >             this.summary.Text = "<b>Event's that fired for current postback
> > (Note: I would only expect 1 because all the dropdowns have
> > autopostback=\"true\")</b>";
> >         }
> >         //set up the event handlers
> >         this.list1.SelectedIndexChanged += new
> > EventHandler(list1_SelectedIndexChanged);
> >         this.list2.SelectedIndexChanged += new
> > EventHandler(list2_SelectedIndexChanged);
> >         //set up the event for the drop down in the repeater
> >         foreach (RepeaterItem item in this.repeat.Items)
> >         {
> >             DropDownList ddl =
> > (DropDownList)item.FindControl("itemTemplateList");
> >             ddl.SelectedIndexChanged += new
> > EventHandler(itemTemplateList_SelectedIndexChanged);
> >         }
> >         base.OnLoad(e);
> >     }
> >
> >     protected void itemTemplateList_SelectedIndexChanged(object sender,
> > EventArgs e)
> >     {
> >         this.summary.Text += "<br/>itemTemplateList_SelectedIndexChanged";
> >     }
> >
> >     protected void list2_SelectedIndexChanged(object sender, EventArgs e)
> >     {
> >         this.summary.Text += "<br/>list2_SelectedIndexChanged";
> >     }
> >
> >     protected void list1_SelectedIndexChanged(object sender, EventArgs e)
> >     {
> >         this.summary.Text += "<br/>list1_SelectedIndexChanged";
> >     }
> >
> >     protected void repeat_ItemDataBound(object sender, RepeaterItemEventArgs
> > e)
> >     {
> >         DropDownList ddl =
> > (DropDownList)e.Item.FindControl("itemTemplateList");
> >         ddl.DataTextField = "Value";
> >         ddl.DataValueField = "Key";
> >         ddl.DataSource = this._repeaterListItems;
> >         ddl.DataBind();
> >     }
> > }
> >
> > </aspx_cs_codebehind>
> >
Author
9 Feb 2006 7:39 PM
Anson Goldade
Hello all,

I've found the solution ... please excuse my sarcasm.  Spending this much
time on the problem and finding out it's an issue that MS just isn't
supporting in their own controls is more than just a litle frustrating.

I finally fired up Lutz (.NET Reflector) and said to myself, hmm, let's see
what the behavior is for the DropDownList.SaveControlState method.  I paste

protected internal virtual object SaveControlState()
{
      return null;
}

Seeing this, it became obvious what the problem was ... MS didn't implement
control state in the DropDownList ... but did decide to implement it in other
controls like the GridView.  I like to refer to this as the
InvalidInternException (originally coined when we tried to serialize an
Oracle exception from the System.Data.Oracle namespace in the 1.0 framework).

So, I created (again, please look past sarcasm for the actual value)
ProperDropDownList.  Here's the code

<properdropdownlist_code>
using System;
using System.Web.UI.WebControls;

namespace TestConcepts
{
    /// <summary>
    /// Summary description for ProperDropDownList
    /// </summary>
    public class ProperDropDownList : DropDownList
    {
        public ProperDropDownList() : base() { }

        //override the control state methods to see if can get the selected
index
        //set prior to the post back happening
        protected override void OnInit(EventArgs e)
        {
            Page.RegisterRequiresControlState(this);
            base.OnInit(e);
        }
        protected override void LoadControlState(object savedState)
        {
            object[] controlState = (object[])savedState;
            base.LoadControlState(controlState[0]);
            base.SelectedIndex = (int)controlState[1];
        }
        protected override object SaveControlState()
        {
            object[] controlState = new object[2];
            controlState[0] = base.SaveControlState();
            controlState[1] = base.SelectedIndex;
            return controlState;
        }
    }
}
</propertdropdonlistcode>

Then I added the following directive to the page.
<%@ register tagprefix="wtf" namespace="TestConcepts" %>

Then I did a find and replace in the aspx page on asp:dropdownlist and
replaced with wtf:properdropdownlist.  Run page and success.  To illustrate
the value of this, turn on viewstate and look at the page size and then turn
off viewstate and compare.  Let me know your thoughts and if you see any
problems with this.  Personally, I believe this behavior should have been
implemented in ListControl so that we can derive the benefits of the Control
State model without having to subclass the dropdown list.

</rant>

Anson Goldade

Show quoteHide quote
"Phillip Williams" wrote:

> Hi Anson,
>
> I put your code on my site to illustrate to you what is happening:
> http://www.webswapp.com/codesamples/testMultipleEventsOnPostBack.aspx
>
> The problem here is that when you set the EnableViewState to false, you have
> reconstructed the dropdownlists on every postback.  After reconstructing
> them, (at the end of the OnPreInit), the selectedindex is always =0.  
> Therefore if you had changed the first dropdownlist (which fired the first
> event) then changed the second dropdownlist, the event of the first would
> still be fired because as far as the page is concerned you have changed both
> dropdownlist from the time you reconstructed them.
>
> Your problem disappeared when you set the EnableViewState to true because
> after the OnPreInit the state of the first dropdownlist (from previous
> postback) changed the selectedIndex of your list and therefore the
> dropdownlist did not sense a change in the selectedindex.
>
> --
> HTH,
> Phillip Williams
> http://www.societopia.net
> http://www.webswapp.com
>
>
> "Anson Goldade" wrote:
>
> > I'm trying to leverage the viewstate optimization capabilities in ASP.NET 2.0
> > but have run into an issue with the DropDownList. 
> >
> > Problem statement
> > SelectedIndexChanged events are getting fired for every dropdown since the
> > page was first rendered.  Here's a sequence example.
> > 1. Load page
> > 2. Change value in ddl #1 ... causes postback.  SelectedIndexChanged fired
> > for ddl #1.
> > 3. Change value in ddl #2 ... causes postback.  SelectedIndexChanged fired
> > for ddl #1 and ddl #2.  This is the unexpected behavior.  Would only expect
> > SelectedIndexChanged to be fired for ddl #2
> >
> > Assumptions
> > 1. Multiple drop down lists on page
> > 2. Autopostback = true
> > 3. EnableViewState on page = false
> > 4. Each ddl has it's own selectedindexchanged event
> > 5. Lists in the ddl's are being rebuilt by overriding OnPreInit on page. 
> > Also tried populating lists in OnInit and OnInitComplete ... same result
> > 6. Subscribe to SelectedIndexChanged event in OnLoad.  Also tried setting
> > onselectedindexchanged properties of ddl in aspx ... same result.
> >
> > I'd like to know what I need to do in order to get only the
> > SelectedIndexChanged event to fire for the control that actually changed.  If
> > I set EnableViewState=true, the expected behavior is exhibited.  I've
> > attached a sample scenario below.  Your help is appreciated.  Thanks.
> >
> > Anson
> >
> > <aspx_page>
> >
> > <%@ Page Language="C#" AutoEventWireup="false"
> > CodeFile="TestMultipleEventsOnPostback.aspx.cs"
> > Inherits="TestMultipleEventsOnPostback" enableviewstate="false" %>
> >
> > <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
> > "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
> >
> > <html xmlns="http://www.w3.org/1999/xhtml" >
> > <head runat="server">
> >     <title>DropDownList SelectedIndexChanged EnableViewState="False"
> > Example</title>
> > </head>
> > <body>
> >     <form id="form1" runat="server">
> >     <div>
> >         <asp:label id="summary" runat="server"></asp:label>
> >         <br />
> >         <br />
> >         <table width="100%">
> >             <tr>
> >                 <td>List #1</td>
> >                 <td>List #2</td>
> >                 <td>Repeater</td>
> >             </tr>
> >             <tr>
> >                 <td><asp:dropdownlist id="list1" runat="server"
> > autopostback="true"></asp:dropdownlist></td>
> >                 <td><asp:dropdownlist id="list2" runat="server"
> > autopostback="true"></asp:dropdownlist></td>
> >                 <td>
> >                     <table>
> >                     <asp:repeater id="repeat" runat="server">
> >                         <itemtemplate>
> >                             <tr>
> >                                 <td><asp:label id="itemTemplateLabel"
> > runat="server"></asp:label></td>
> >                                 <td><asp:dropdownlist id="itemTemplateList"
> > runat="server" autopostback="true"></asp:dropdownlist></td>
> >                             </tr>
> >                         </itemtemplate>
> >                     </asp:repeater>
> >                     </table>
> >                 </td>
> >             </tr>
> >         </table>
> >        
> >     </div>
> >     </form>
> > </body>
> > </html>
> >
> > </aspx_page>
> >
> > <aspx_cs_codebehind>
> >
> > using System;
> > using System.Collections.Generic;
> > using System.Data;
> > using System.Configuration;
> > using System.Collections;
> > using System.Web;
> > using System.Web.Security;
> > using System.Web.UI;
> > using System.Web.UI.WebControls;
> > using System.Web.UI.WebControls.WebParts;
> > using System.Web.UI.HtmlControls;
> >
> > public partial class TestMultipleEventsOnPostback : System.Web.UI.Page
> > {
> >     //items for list #1
> >     private Dictionary<string, string> _list1Items = new Dictionary<string,
> > string>();
> >     //items for list #2
> >     private Dictionary<string, string> _list2Items = new Dictionary<string,
> > string>();
> >     //items for repeater drop down
> >     private Dictionary<string, string> _repeaterListItems = new
> > Dictionary<string, string>();
> >     //repeater source just to get 5 items created
> >     private object[] _repeaterSource = new object[5];
> >     public TestMultipleEventsOnPostback()
> >     {
> >         //create the list 1 items
> >         for (int i = 0; i <= 10; i++)
> >         {
> >             this._list1Items.Add(i.ToString(), string.Format("Value = {0}",
> > i));
> >         }
> >         //create the list 2 items
> >         for (int i = 0; i <= 40; i++)
> >         {
> >             this._list2Items.Add(i.ToString(), string.Format("Value = {0}",
> > i));
> >         }
> >         //create the list repeater items
> >         for (int i = 0; i <= 60; i++)
> >         {
> >             this._repeaterListItems.Add(i.ToString(), string.Format("Value =
> > {0}", i));
> >         }
> >     }
> >     protected override void OnPreInit(EventArgs e)
> >     {
> >         //populate the drop down lists and rebuild the repeater
> >         this.list1.DataTextField = "Value";
> >         this.list1.DataValueField = "Key";
> >         this.list1.DataSource = this._list1Items;
> >         this.list1.DataBind();
> >
> >         this.list2.DataTextField = "Value";
> >         this.list2.DataValueField = "Key";
> >         this.list2.DataSource = this._list2Items;
> >         this.list2.DataBind();
> >
> >         //rebuild the repeater control
> >         this.repeat.ItemDataBound += new
> > RepeaterItemEventHandler(repeat_ItemDataBound);
> >         this.repeat.DataSource = this._repeaterSource;
> >         this.repeat.DataBind();
> >
> >         base.OnPreInit(e);
> >     }
> >     protected override void OnLoad(EventArgs e)
> >     {
> >         if (!this.IsPostBack)
> >         {
> >             this.summary.Text = "No postback";
> >         }
> >         else
> >         {
> >             //clear out the summary label
> >             this.summary.Text = "<b>Event's that fired for current postback
> > (Note: I would only expect 1 because all the dropdowns have
> > autopostback=\"true\")</b>";
> >         }
> >         //set up the event handlers
> >         this.list1.SelectedIndexChanged += new
> > EventHandler(list1_SelectedIndexChanged);
> >         this.list2.SelectedIndexChanged += new
> > EventHandler(list2_SelectedIndexChanged);
> >         //set up the event for the drop down in the repeater
> >         foreach (RepeaterItem item in this.repeat.Items)
> >         {
> >             DropDownList ddl =
> > (DropDownList)item.FindControl("itemTemplateList");
> >             ddl.SelectedIndexChanged += new
> > EventHandler(itemTemplateList_SelectedIndexChanged);
> >         }
> >         base.OnLoad(e);
> >     }
> >
> >     protected void itemTemplateList_SelectedIndexChanged(object sender,
> > EventArgs e)
> >     {
> >         this.summary.Text += "<br/>itemTemplateList_SelectedIndexChanged";
> >     }
> >
> >     protected void list2_SelectedIndexChanged(object sender, EventArgs e)
> >     {
> >         this.summary.Text += "<br/>list2_SelectedIndexChanged";
> >     }
> >
> >     protected void list1_SelectedIndexChanged(object sender, EventArgs e)
> >     {
> >         this.summary.Text += "<br/>list1_SelectedIndexChanged";
> >     }
> >
> >     protected void repeat_ItemDataBound(object sender, RepeaterItemEventArgs
> > e)
> >     {
> >         DropDownList ddl =
> > (DropDownList)e.Item.FindControl("itemTemplateList");
> >         ddl.DataTextField = "Value";
> >         ddl.DataValueField = "Key";
> >         ddl.DataSource = this._repeaterListItems;
> >         ddl.DataBind();
> >     }
> > }
> >
> > </aspx_cs_codebehind>
> >
Author
9 Feb 2006 9:10 PM
Phillip Williams
Anson,

This is excellent.  I just started down the road of extending the
dropdownlist.  Then I went into a meeting before doing much about it.  Now I
read your solution and I think it is a great analysis and solution. 

Show quoteHide quote
"Anson Goldade" wrote:

> Hello all,
>
> I've found the solution ... please excuse my sarcasm.  Spending this much
> time on the problem and finding out it's an issue that MS just isn't
> supporting in their own controls is more than just a litle frustrating.
>
> I finally fired up Lutz (.NET Reflector) and said to myself, hmm, let's see
> what the behavior is for the DropDownList.SaveControlState method.  I paste
>
> protected internal virtual object SaveControlState()
> {
>       return null;
> }
>
> Seeing this, it became obvious what the problem was ... MS didn't implement
> control state in the DropDownList ... but did decide to implement it in other
> controls like the GridView.  I like to refer to this as the
> InvalidInternException (originally coined when we tried to serialize an
> Oracle exception from the System.Data.Oracle namespace in the 1.0 framework).
>
> So, I created (again, please look past sarcasm for the actual value)
> ProperDropDownList.  Here's the code
>
> <properdropdownlist_code>
> using System;
> using System.Web.UI.WebControls;
>
> namespace TestConcepts
> {
>     /// <summary>
>     /// Summary description for ProperDropDownList
>     /// </summary>
>     public class ProperDropDownList : DropDownList
>     {
>         public ProperDropDownList() : base() { }
>
>         //override the control state methods to see if can get the selected
> index
>         //set prior to the post back happening
>         protected override void OnInit(EventArgs e)
>         {
>             Page.RegisterRequiresControlState(this);
>             base.OnInit(e);
>         }
>         protected override void LoadControlState(object savedState)
>         {
>             object[] controlState = (object[])savedState;
>             base.LoadControlState(controlState[0]);
>             base.SelectedIndex = (int)controlState[1];
>         }
>         protected override object SaveControlState()
>         {
>             object[] controlState = new object[2];
>             controlState[0] = base.SaveControlState();
>             controlState[1] = base.SelectedIndex;
>             return controlState;
>         }
>     }
> }
> </propertdropdonlistcode>
>
> Then I added the following directive to the page.
> <%@ register tagprefix="wtf" namespace="TestConcepts" %>
>
> Then I did a find and replace in the aspx page on asp:dropdownlist and
> replaced with wtf:properdropdownlist.  Run page and success.  To illustrate
> the value of this, turn on viewstate and look at the page size and then turn
> off viewstate and compare.  Let me know your thoughts and if you see any
> problems with this.  Personally, I believe this behavior should have been
> implemented in ListControl so that we can derive the benefits of the Control
> State model without having to subclass the dropdown list.
>
> </rant>
>
> Anson Goldade
>
> "Phillip Williams" wrote:
>
> > Hi Anson,
> >
> > I put your code on my site to illustrate to you what is happening:
> > http://www.webswapp.com/codesamples/testMultipleEventsOnPostBack.aspx
> >
> > The problem here is that when you set the EnableViewState to false, you have
> > reconstructed the dropdownlists on every postback.  After reconstructing
> > them, (at the end of the OnPreInit), the selectedindex is always =0.  
> > Therefore if you had changed the first dropdownlist (which fired the first
> > event) then changed the second dropdownlist, the event of the first would
> > still be fired because as far as the page is concerned you have changed both
> > dropdownlist from the time you reconstructed them.
> >
> > Your problem disappeared when you set the EnableViewState to true because
> > after the OnPreInit the state of the first dropdownlist (from previous
> > postback) changed the selectedIndex of your list and therefore the
> > dropdownlist did not sense a change in the selectedindex.
> >
> > --
> > HTH,
> > Phillip Williams
> > http://www.societopia.net
> > http://www.webswapp.com
> >
> >
> > "Anson Goldade" wrote:
> >
> > > I'm trying to leverage the viewstate optimization capabilities in ASP.NET 2.0
> > > but have run into an issue with the DropDownList. 
> > >
> > > Problem statement
> > > SelectedIndexChanged events are getting fired for every dropdown since the
> > > page was first rendered.  Here's a sequence example.
> > > 1. Load page
> > > 2. Change value in ddl #1 ... causes postback.  SelectedIndexChanged fired
> > > for ddl #1.
> > > 3. Change value in ddl #2 ... causes postback.  SelectedIndexChanged fired
> > > for ddl #1 and ddl #2.  This is the unexpected behavior.  Would only expect
> > > SelectedIndexChanged to be fired for ddl #2
> > >
> > > Assumptions
> > > 1. Multiple drop down lists on page
> > > 2. Autopostback = true
> > > 3. EnableViewState on page = false
> > > 4. Each ddl has it's own selectedindexchanged event
> > > 5. Lists in the ddl's are being rebuilt by overriding OnPreInit on page. 
> > > Also tried populating lists in OnInit and OnInitComplete ... same result
> > > 6. Subscribe to SelectedIndexChanged event in OnLoad.  Also tried setting
> > > onselectedindexchanged properties of ddl in aspx ... same result.
> > >
> > > I'd like to know what I need to do in order to get only the
> > > SelectedIndexChanged event to fire for the control that actually changed.  If
> > > I set EnableViewState=true, the expected behavior is exhibited.  I've
> > > attached a sample scenario below.  Your help is appreciated.  Thanks.
> > >
> > > Anson
> > >
> > > <aspx_page>
> > >
> > > <%@ Page Language="C#" AutoEventWireup="false"
> > > CodeFile="TestMultipleEventsOnPostback.aspx.cs"
> > > Inherits="TestMultipleEventsOnPostback" enableviewstate="false" %>
> > >
> > > <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
> > > "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
> > >
> > > <html xmlns="http://www.w3.org/1999/xhtml" >
> > > <head runat="server">
> > >     <title>DropDownList SelectedIndexChanged EnableViewState="False"
> > > Example</title>
> > > </head>
> > > <body>
> > >     <form id="form1" runat="server">
> > >     <div>
> > >         <asp:label id="summary" runat="server"></asp:label>
> > >         <br />
> > >         <br />
> > >         <table width="100%">
> > >             <tr>
> > >                 <td>List #1</td>
> > >                 <td>List #2</td>
> > >                 <td>Repeater</td>
> > >             </tr>
> > >             <tr>
> > >                 <td><asp:dropdownlist id="list1" runat="server"
> > > autopostback="true"></asp:dropdownlist></td>
> > >                 <td><asp:dropdownlist id="list2" runat="server"
> > > autopostback="true"></asp:dropdownlist></td>
> > >                 <td>
> > >                     <table>
> > >                     <asp:repeater id="repeat" runat="server">
> > >                         <itemtemplate>
> > >                             <tr>
> > >                                 <td><asp:label id="itemTemplateLabel"
> > > runat="server"></asp:label></td>
> > >                                 <td><asp:dropdownlist id="itemTemplateList"
> > > runat="server" autopostback="true"></asp:dropdownlist></td>
> > >                             </tr>
> > >                         </itemtemplate>
> > >                     </asp:repeater>
> > >                     </table>
> > >                 </td>
> > >             </tr>
> > >         </table>
> > >        
> > >     </div>
> > >     </form>
> > > </body>
> > > </html>
> > >
> > > </aspx_page>
> > >
> > > <aspx_cs_codebehind>
> > >
> > > using System;
> > > using System.Collections.Generic;
> > > using System.Data;
> > > using System.Configuration;
> > > using System.Collections;
> > > using System.Web;
> > > using System.Web.Security;
> > > using System.Web.UI;
> > > using System.Web.UI.WebControls;
> > > using System.Web.UI.WebControls.WebParts;
> > > using System.Web.UI.HtmlControls;
> > >
> > > public partial class TestMultipleEventsOnPostback : System.Web.UI.Page
> > > {
> > >     //items for list #1
> > >     private Dictionary<string, string> _list1Items = new Dictionary<string,
> > > string>();
> > >     //items for list #2
> > >     private Dictionary<string, string> _list2Items = new Dictionary<string,
> > > string>();
> > >     //items for repeater drop down
> > >     private Dictionary<string, string> _repeaterListItems = new
> > > Dictionary<string, string>();
> > >     //repeater source just to get 5 items created
> > >     private object[] _repeaterSource = new object[5];
> > >     public TestMultipleEventsOnPostback()
> > >     {
> > >         //create the list 1 items
> > >         for (int i = 0; i <= 10; i++)
> > >         {
> > >             this._list1Items.Add(i.ToString(), string.Format("Value = {0}",
> > > i));
> > >         }
> > >         //create the list 2 items
> > >         for (int i = 0; i <= 40; i++)
> > >         {
> > >             this._list2Items.Add(i.ToString(), string.Format("Value = {0}",
> > > i));
> > >         }
> > >         //create the list repeater items
> > >         for (int i = 0; i <= 60; i++)
> > >         {
> > >             this._repeaterListItems.Add(i.ToString(), string.Format("Value =
> > > {0}", i));
> > >         }
> > >     }
> > >     protected override void OnPreInit(EventArgs e)
> > >     {
> > >         //populate the drop down lists and rebuild the repeater
> > >         this.list1.DataTextField = "Value";
> > >         this.list1.DataValueField = "Key";
> > >         this.list1.DataSource = this._list1Items;
> > >         this.list1.DataBind();
> > >
> > >         this.list2.DataTextField = "Value";
> > >         this.list2.DataValueField = "Key";
> > >         this.list2.DataSource = this._list2Items;
> > >         this.list2.DataBind();
> > >
> > >         //rebuild the repeater control
> > >         this.repeat.ItemDataBound += new
> > > RepeaterItemEventHandler(repeat_ItemDataBound);
> > >         this.repeat.DataSource = this._repeaterSource;
> > >         this.repeat.DataBind();
> > >
> > >         base.OnPreInit(e);
> > >     }
> > >     protected override void OnLoad(EventArgs e)
> > >     {
> > >         if (!this.IsPostBack)
> > >         {
> > >             this.summary.Text = "No postback";
> > >         }
> > >         else
> > >         {
> > >             //clear out the summary label
> > >             this.summary.Text = "<b>Event's that fired for current postback
> > > (Note: I would only expect 1 because all the dropdowns have
> > > autopostback=\"true\")</b>";
> > >         }
> > >         //set up the event handlers
> > >         this.list1.SelectedIndexChanged += new
> > > EventHandler(list1_SelectedIndexChanged);
> > >         this.list2.SelectedIndexChanged += new
> > > EventHandler(list2_SelectedIndexChanged);
> > >         //set up the event for the drop down in the repeater
> > >         foreach (RepeaterItem item in this.repeat.Items)
> > >         {
> > >             DropDownList ddl =
> > > (DropDownList)item.FindControl("itemTemplateList");
> > >             ddl.SelectedIndexChanged += new
> > > EventHandler(itemTemplateList_SelectedIndexChanged);
> > >         }
> > >         base.OnLoad(e);
> > >     }
> > >
> > >     protected void itemTemplateList_SelectedIndexChanged(object sender,
> > > EventArgs e)
> > >     {
> > >         this.summary.Text += "<br/>itemTemplateList_SelectedIndexChanged";
> > >     }
> > >
> > >     protected void list2_SelectedIndexChanged(object sender, EventArgs e)
> > >     {
> > >         this.summary.Text += "<br/>list2_SelectedIndexChanged";
> > >     }
> > >
> > >     protected void list1_SelectedIndexChanged(object sender, EventArgs e)