【问题标题】:DropDownList OnSelectedIndexChange to 0th index w/out ViewStateDropDownList OnSelectedIndexChange to 0th index w/out ViewState
【发布时间】:2009-01-23 20:21:29
【问题描述】:

我确实关注了文章TRULLY Understanding ViewState(顺便说一句很棒的文章),并且填充我的下拉列表效果很好。我什至设置了一个几乎一样好的 OnSelectedIndexChange 事件。

我发现的问题是 SelectedIndexChanged 事件在选择第 0 个索引时不会触发。但是,其他时间都是这样。

这里有一些代码:

<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 引发了一个事件,该事件被该控件所在的父页面捕获,以便父页面可以使用新选择的值执行一些操作。这目前适用于所选索引 > 0 的所有情况。

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

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

【问题讨论】:

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


    【解决方案1】:

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

    zero [selected]
    one
    two
    

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

    zero
    one [selected]
    two
    

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

    zero [selected]
    one
    two
    

    “零”是选定的值,因为这是加载数据时的默认值。然后 ASP.NET 在 Request 中查找 __EVENTTARGET__EVENTARGUMENT 并找到下拉列表的 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 将为您维护数据以及选定的索引,以便在您回发时下拉列表会将新索引与您在客户端中看到的索引进行比较。

    【讨论】:

      【解决方案2】:

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

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

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

          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 个索引被恢复时),当我们最终进入 Events 阶段时,控件没有检测到变化。

      不确定这是不是最优雅的路线,但到目前为止它运行良好。

      然后我在 IPostBackDataHandler 的 msdn 文档中找到了这个:

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

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

      【讨论】:

      • +1 非常好 - 这是在没有 ViewState 的情况下实现它的绝佳方式!抱歉,我没有注意到您不想要 ViewState - 下次我会更仔细地阅读这个问题。
      • 感谢您的初步解决方案,它确实有助于阐明事情的顺序。我想我并不像我想象的那样了解 asp.net 页面的生命周期。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-10
      • 2018-10-14
      • 2019-06-28
      • 1970-01-01
      • 2013-04-27
      • 1970-01-01
      • 2021-10-27
      相关资源
      最近更新 更多