This seems like a common scenario with an obvious solution, but somehow I haven’t encountered it.
I have a DropDownList with an event handler bound to the SelectedIndexChanged event and AutoPostback="true", which works as intended.
The event handler is executed when the value has changed, but if the value is changed via script using jQuery on the client-side, and a post back is subsequently triggered when the user changes the value (and the drop-down’s onchange event fires), then the server-side code detects that the value hasn’t changed since the server last saw its state, and therefore the event handler isn’t run on the server side.
It seems like something that would be loaded via view state, but disabling ViewState has no effect.
- The page renders the drop-down with the value “A” selected.
- The user changes the drop-down list to value “B”.
- The value is automatically posted to the server, and the
SelectedIndexChangedevent handler is executed. - A client script is run to change the value on the client back to “A” using
jQuery.val(). - The user changes the value back to “B”.
- The value is automatically posted to the server, but because the value was “B” when the server last rendered the page, the
SelectedIndexChangedevent handler is not executed.
Client-side
<asp:DropDownList ID="dlst" runat="server" AutoPostBack="true" OnSelectedIndexChanged="dlst_SelectedIndexChanged">
<asp:ListItem Text="A" Value="A" />
<asp:ListItem Text="B" Value="B" />
</asp:DropDownList>
<asp:Button ID="btnChange" runat="server" Text="Change" OnClientClick="return changeDDL(this,event)" />
<script type="text/javascript">
function changeDDL(sender, e) {
var dlst = $("#<%= dlst.ClientID %>");
dlst.val(dlst.val() === "A" ? "B" : "A");
return false;
}
</script>
Server-side
protected void dlst_SelectedIndexChanged(object sender, EventArgs e)
{
btnChange.Text = dlst.SelectedValue == "A" ? "Change B" : "Change A";
}
After debugging the .Net Framework Reference Source, I’ve concluded the problem is deep in the
DropDownListimplementation, and can’t be remedied without reimplementing or inheriting the class to change the behavior.In the
DropDownListclass, there is aLoadPostData()implementation of theIPostBackDataHandlerinterface method.Inside
LoadPostData(), the index of the value currently selected in the drop-down is compared against theSelectedIndexproperty that is either loaded fromViewStateor defaulted to0whenEnableViewState="false"is set on the drop-down.The
OnSelectedIndexChanged()method is called by theRaisePostDataChangedEvent(), which, as the name indicates, is only called if the post data has changed in comparison to the viewstate/default data. Therefore, if the currently selected index matches the viewstate/default index, theSelectedIndexChangedevent doesn’t fire.In my case, two things can cause the issue:
ViewStateenabled, the current index value matches theSelectedIndexvalue loaded fromViewState, soSelectedIndexChangeddoesn’t fire.ViewStatedisabled, theSelectedIndexvalue is defaulted to0, so when the current index value is0,SelectedIndexChangeddoesn’t fire.DropDownList.LoadPostData()
Later, when the
Pageclass is going through the controls whose post data has changed, the drop-down’sOnSelectedIndexChangedmethod is called if a change was detected. Since the drop-down’s post data hasn’t changed in this case, the event isn’t raised.DropDownList.RaisePostDataChangedEvent()