DropDownList SelectedIndexChanged到没有ViewState的第0个索引

时间:2009-01-23 20:21:29

标签: asp.net user-controls drop-down-menu viewstate selectedindexchanged

我确实按照文章TRULLY Understanding ViewState(很棒的文章btw)填写我的下拉列表工作得很好。我甚至设置了一个OnSelectedIndexChange事件,它几乎同样激发。

我发现的问题是在选择第0个索引时不会触发SelectedIndexChanged事件。然而,它会做所有其他时间。

以下是一些代码:

<asp:DropDownList runat="server" ID="DropDownList1" EnableViewState="false" 
AutoPostBack="True" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" />


protected override void OnInit(EventArgs e)
{
    this.DropDownList1.DataTextField = "Text";
    this.DropDownList1.DataValueField = "Value";
    this.DropDownList1.DataSource = fillQueueDropDown();
    this.DropDownList1.DataBind();

    base.OnInit(e);
}

protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
    OnSelectedQueueChanged(e);
}

public void OnSelectedQueueChanged(EventArgs e)
    {
        // Do stuff.
    }

public event EventHandler queueNamesChangedEvent;
public void OnSelectedQueueChanged(EventArgs e)
    {
        if (queueNamesChangedEvent != null)
            queueNamesChangedEvent(this, e);
    }

我想我可以在Page_Load方法中进行某种类型的检查:

  if(ViewState["selectedIndexChangedFlag"] != 1)
      // raise OnSelectedChange event

或者我可以在OnInit()方法中设置一些东西,我每次都可以重新绑定这些数据吗?

请参阅,我的自定义EventHander引发一个事件,该事件由此控件所在的父页面捕获,以便父级可以使用新选择的值执行某些操作。并且这适用于所选索引&gt;的所有情况。 0

我在此控件中创建了一个属性,其中包含最近选择的索引,在这种情况下,我的父页面可以对每个Page_Load ... dunno上的此属性值执行操作。

接受建议。或者如何强制此SelectedIndexChanged事件触发第0个索引选择。

2 个答案:

答案 0 :(得分:8)

问题是您每次都在加载数据,而这正在重置所选索引。想象一下这是你的下拉:

zero [selected]
one
two

然后在客户端中更改所选索引:

zero
one [selected]
two

这会使用您的新索引(1)填充隐藏的输入__EVENTARGUMENT,并使用下拉列表的__EVENTTARGET填充隐藏的输入id。现在,服务器端代码启动并重新加载您的数据:

zero [selected]
one
two

“零”是选定的值,因为这是加载数据时的默认值。然后,ASP.NET在__EVENTTARGET中查找__EVENTARGUMENTRequest,找到您的下拉列表id并找到新索引(1)。现在您的下拉列表如下所示:

zero 
one [selected]
two

由于索引已更改,因此下拉列表会引发其SelectedIndexChanged事件,指示索引已更改。显然这是正在运行的部分,现在让我们看看为什么选择列表中的第一项不会引发事件。

现在我们可以说我们仍处于其所处状态的下拉状态(选择“一个”并且选择的索引为1)。当我们在客户端上选择列表中的第一项时会发生什么?

__EVENTTARGET__EVENTARGUMENT填充了下拉列表的id和新索引(0)。然后服务器将数据加载到下拉列表中,下拉列表现在再次显示如下:

zero [selected]
one
two

请注意,由于您在之前重新加载了数据,因此触发的索引已经设置为0,因为这是默认设置。现在,当您的事件触发并且下拉列表的选定索引设置为0时,下拉列表不会将此视为更改,因为所选索引(据其所知)尚未更改。

以下是解决问题的方法:

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    if (!Page.IsPostBack)
    {
        this.DropDownList1.DataTextField = "Text";
        this.DropDownList1.DataValueField = "Value";
        this.DropDownList1.DataSource = fillQueueDropDown();
        this.DropDownList1.DataBind();
    }    
}

如果页面不是回发,这样做只会将数据加载到下拉列表中。这意味着ViewState将为您维护数据以及所选索引,以便在您回发下拉列表时将新索引与您在客户端中看到的索引进行比较。

答案 1 :(得分:5)

我在此下拉列表中禁用ViewState的目的是最小化页面的ViewState大小。

我只使用if(!Page.IsPostBack){... DataBind()...}时遇到的问题是,当你第一次选择一个项目并且页面重新加载时,我的下拉列表变空了。

我最终做的是在此控件上创建另一个属性LastIndex。触发OnSelectedIndexChanged事件时,我更新LastIndex值。在Page_Load中,我比较Current和Last索引值,如果它们不同,则触发Index change事件。

    public int SelectedValue{
        get { return this.DropDownList1.SelectedItem.Value; }
    }

    public int LastIndex{
        get { return this.ViewState["lastIndex"] == null ? -1 : (int)this.ViewState["lastIndex"]; }
        set { this.ViewState["lastIndex"] = value; }
    }

    protected override void OnInit(EventArgs e){
        base.OnInit(e);
        this.DropDownList1.DataTextField = "Text";
        this.DropDownList1.DataValueField = "Value";
        this.DropDownList1.DataSource = fillQueueDropDown();
        this.DropDownList1.DataBind();
    }

    protected void Page_Load(object sender, EventArgs e){
        if (this.LastIndex != this.SelectedValue)
            this.OnSelectedQueueChanged(new EventArgs());
    }

    private ListItemCollection fillQueueDropDown(){...}

    protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e){
        OnSelectedQueueChanged(e);
        this.LastIndex = this.SelectedValue;
    }

    public event EventHandler queueNamesChangedEvent;
    public void OnSelectedQueueChanged(EventArgs e){
        if (queueNamesChangedEvent != null)
            queueNamesChangedEvent(this, e);
    }

你是对的。数据在OnInit阶段重新加载并重新绑定。然后恢复ViewState(当第0个索引恢复时),当我们最终进入事件阶段时,控件不会检测到更改。

不确定这是最优雅的路线,但到目前为止效果还不错。

然后我在msdn docs for IPostBackDataHandler中找到了这个:

  public virtual bool LoadPostData(string postDataKey, 
     NameValueCollection postCollection) {

     String presentValue = Text;
     String postedValue = postCollection[postDataKey];

     if (presentValue == null || !presentValue.Equals(postedValue)) {
        Text = postedValue;
        return true;
     }

     return false;
  }

由于当前值与更改为的值相同,因此不会触发事件。