这似乎是一个常见的情况,有一个明显的解决方案,但是以某种方式我还没有遇到过。

我有一个DropDownList,其中有一个绑定到SelectedIndexChanged事件和AutoPostback="true"的事件处理程序,它可以按预期工作。

值更改后将执行事件处理程序,但是如果在客户端使用jQuery通过脚本更改了值,则当用户更改值(以及下拉菜单的onchange事件)时,随后会触发回发触发),则服务器端代码会检测到自服务器上次看到其状态以来该值未更改,因此事件处理程序未在服务器端运行。

似乎可以通过视图状态加载某些内容,但是禁用ViewState无效。


该页面使用选择的值“ A”呈现下拉菜单。
用户将下拉列表更改为值“ B”。
该值将自动发布到服务器,并执行SelectedIndexChanged事件处理程序。
运行客户端脚本,使用jQuery.val()将客户端上的值更改回“ A”。
用户将值改回“ B”。
该值自动发布到服务器,但是由于服务器上次渲染页面时该值为“ B”,因此不会执行SelectedIndexChanged事件处理程序。


客户端

<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>


服务器端

protected void dlst_SelectedIndexChanged(object sender, EventArgs e)
{
    btnChange.Text = dlst.SelectedValue == "A" ? "Change B" : "Change A";
}

最佳答案

在调试.Net Framework Reference Source之后,我得出的结论是DropDownList实现中存在深层问题,如果不重新实现或继承该类以更改行为,则无法解决该问题。

DropDownList类中,有LoadPostData()接口方法的IPostBackDataHandler实现。

LoadPostData()中,将下拉菜单中当前选择的值的索引与SelectedIndex属性进行比较,该属性是从ViewState加载的,或者在下拉菜单中设置了0时默认为EnableViewState="false"

OnSelectedIndexChanged()方法由RaisePostDataChangedEvent()调用,顾名思义,该方法仅在发布数据与viewstate / default数据相比已更改的情况下才调用。因此,如果当前选择的索引与viewstate / default索引匹配,则不会触发SelectedIndexChanged事件。

就我而言,有两个原因可以导致此问题:


启用ViewState时,当前索引值与从SelectedIndex加载的ViewState值匹配,因此不会触发SelectedIndexChanged
禁用ViewState时,SelectedIndex值默认为0,因此当当前索引值为0时,SelectedIndexChanged不会触发。


DropDownList.LoadPostData()

protected virtual bool LoadPostData(String postDataKey, NameValueCollection postCollection) {
    //{snip...}
    int n = Items.FindByValueInternal(selectedItems[0], false);

    if (SelectedIndex != n) {
        SetPostDataSelection(n); // Calls ListControl.SetPostDataSelection() and sets Items[selectedIndex].Selected = true;
        return true;
    }

    return false;
}


稍后,当Page类通过其后数据已更改的控件时,如果检测到更改,则调用下拉菜单的OnSelectedIndexChanged方法。由于在这种情况下下拉列表的发布数据未更改,因此不会引发该事件。

DropDownList.RaisePostDataChangedEvent()

protected virtual void RaisePostDataChangedEvent() {
    //{snip...}
    OnSelectedIndexChanged(EventArgs.Empty);
}

09-27 00:53