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'开发者_如何学Ct 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."
Change events in HTML server controls and Web server controls, such as the TextBox control, do not immediately cause a post. Instead, they are raised the next time a post occurs.
When users click your 'Edit Artist' button, ASP.NET thinks lbLinks.SelectedIndex
has changed, so it invokes its SelectedIndexChanged
handler.
The reason ASP.NET thinks the index has changed is this: when the page first loads, lbLinks
doesn'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>
element lbLinks
has a non-empty value, and it is submitted as part of the post data. Take a look at Request.Form["lbLinks"]
and you'll see that it equals lbLinks.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 SelectedIndex
for all your ListBox
controls when you're using the OnSelectedIndexChanged
event, 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 OnSelectedIndexChanged
events will both fire every time you click the button.)
According to the page, both listboxes have changed. It was selectedIndex -1, then when the page loads they will both go to selected index 0, possibly due to the manner in which you are assigning data to them. So when a postback occurs it only makes sense for both of the methods to run since their selected index has actually changed.
See if the following makes your problem go away. Set the selected index as zero:
if (!IsPostBack){
GetArtists(); // populates artists listbox
GetLinks(); // populates links listbox
lbArtists.SelectedIndex = 0;
lbLinks.SelectedIndex = 0;
}
Are you sure you haven't changed the index on the other listbox? Are you re-binding that list box, maybe, because that code get executed on post-pack?
I'm guessing you call LoadLinks() in your Page_Load() or other similar event. Remember, when you do a postback, it has to re-run the entire page lifecycle. You're working with a brand new instance of the page class. This is true even if you just wanted to process a simple button click or selection change event.
Try seeing the call stack & see why the unrelated method is being called.
I am guessing that it could be the code that changes the Index
property of the other listbox & then wiring of eventhandlers after that.
Call stack should help you identify the reason, to begin with.
The problem is in your AutoPostBack="true" declarations. I don't know the exact reason if anyone else would like to elaborate, but when multiple controls on a page have AutoPostBack="true", all server side events are fired when posting back to the server.
EDIT My guess is that for some reason, possibly related to view state the SelectedIndexChanged is evaluating to true for both controls on postback.
My thought is that the second list box doesn't start with a selected item. When the page loads, your browser is selecting the first item automatically. When you post the page, the viewstate has changed (no item to first item) so the postback event occurs. Try making sure you've got a selection.
精彩评论