I’m sure this is simple but it’s driving me nuts.
I have a ListBox on my page to show artists, which calls a method when the index is changed, and a button which loads an artist from that list in another page when clicked:
<asp:ListBox ID="lbArtists" runat="server" Rows="1" AutoPostBack="true" OnSelectedIndexChanged="ShowArtistsWorks" />
<asp:Button ID="btnEditArtist" runat="server" Text="Edit the artist" OnClick="LoadArtist" />
Further on, I have a similar list of links, which also has an autopostback method:
<asp:ListBox ID="lbLinks" runat="server" Rows="1" AutoPostBack="true" OnSelectedIndexChanged="LoadLink" />
The problem is, when I invoke ShowArtistsWorks() by clicking btnEditArtist, the LoadLink() method also gets called. Why is that happening? Why would that get called when I haven’t changed the index on the lbLinks ListBox? It shouldn’t be going near that method.
EDIT: (relevant) Code-behind methods (
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack){
GetArtists(); // populates artists listbox
GetLinks(); // populates links listbox
}
}
protected void LoadArtist(object sender, EventArgs e){
if (lbArtists.SelectedValue != "")
Response.Redirect("Artist.aspx?id=" + lbArtists.SelectedValue);
}
protected void LoadLink(object sender, EventArgs e)
{
if (lbLinks.SelectedValue != "")
Response.Redirect("Link.aspx?id=" + lbLinks.SelectedValue);
}
EDIT #2: I could easily kludge a fix for this in the individual methods to stop them happening when they shouldn’t, but I want to understand why methods that I don’t call, and that only get called from one place, get invoked inadvertently.
ACCEPTED ANSWER: Even though Boon (now CRice) got in first with an explanation and a solution, I decided to accept Jeff’s more thorough explanation because that was what I wanted, a more in-depth analysis. Thanks to everyone who answered.
Change events are raised on every postback for which they’re relevant – as described in the MSDN topic “ASP.NET Web Server Control Event Model.”
When users click your ‘Edit Artist’ button, ASP.NET thinks
lbLinks.SelectedIndexhas changed, so it invokes itsSelectedIndexChangedhandler.The reason ASP.NET thinks the index has changed is this: when the page first loads,
lbLinksdoesn’t have a selected index (or value) unless you say otherwise by explicitly setting it. Until you do that, the selected index is -1 and its selected value is an empty string. The selected value (in this case, an empty string) is written to view state when the page is rendered so that ASP.NET can tell if the value has changed on postbacks.You can observe this while debugging by inspecting your list boxes’ selected indices and values before rendering, or you can use one of the online view state decoders (like this one) to see what’s in your page when it’s first written (though to read this, you need to know about the structure of serialized view state data).
When you next post back, the HTML
<select>elementlbLinkshas a non-empty value, and it is submitted as part of the post data. Take a look atRequest.Form["lbLinks"]and you’ll see that it equalslbLinks.Items[0].Value.ASP.NET maps the posted value to
lbLinks.SelectedValue, but it also knows that the selected value used to be an empty string – it gets the old value from view state. Since the two values are different, the process raises the control’s selected index changed event, causing the undesirable behavior you’ve observed.As boon suggested, the solution is to always explicitly set the
SelectedIndexfor all yourListBoxcontrols when you’re using theOnSelectedIndexChangedevent, even if you’re just setting the index to zero.(The AutoPostBack setting is an unrelated red herring. If you remove it from both list boxes, their
OnSelectedIndexChangedevents will both fire every time you click the button.)