DropDownList OnSelectedIndexChange на 0-й индекс без ViewState

Я следил за статьей ДЕЙСТВИТЕЛЬНО Понимание 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-го индекса.


person Dave Baghdanov    schedule 23.01.2009    source источник


Ответы (2)


Проблема в том, что вы загружаете данные каждый раз, и это сбрасывает выбранный индекс. Представьте, что это ваш раскрывающийся список:

zero [selected]
one
two

Затем в клиенте вы меняете выбранный индекс:

zero
one [selected]
two

Это заполнит скрытый ввод __EVENTARGUMENT вашим новым индексом (1), а скрытый ввод __EVENTTARGETid вашего раскрывающегося списка. Теперь запускается серверный код и перезагружает ваши данные:

zero [selected]
one
two

«ноль» — это выбранное значение, потому что это значение по умолчанию при загрузке данных. Затем ASP.NET ищет __EVENTTARGET и __EVENTARGUMENT в Request, находит раскрывающийся список 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 будет поддерживать данные для вас, а также выбранный индекс, так что, когда вы отправите обратно, раскрывающийся список сравнит новый индекс с индексом, который вы видели в клиенте.

person Andrew Hare    schedule 23.01.2009

Моя цель с отключением ViewState в этом раскрывающемся списке — минимизировать размер ViewState для страницы.

Проблема, с которой я столкнулся только при выполнении if(!Page.IsPostBack){...DataBind()...}, заключается в том, что когда вы выбираете элемент в первый раз и страница перезагружается, мой раскрывающийся список становится пустым .

В итоге я создал еще одно свойство для этого элемента управления, LastIndex. Когда срабатывает событие OnSelectedIndexChanged, я обновляю значение LastIndex. В Page_Load я сравниваю текущие и последние значения индекса, если они разные, затем запускаю событие изменения индекса.

    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, элемент управления не обнаруживает изменения.

Не уверен, что это самый элегантный маршрут, но пока он работает хорошо.

Затем я нашел это в документах msdn для 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;
  }

Поскольку текущее значение совпадает с измененным значением, событие не запускается.

person Dave Baghdanov    schedule 24.01.2009
comment
+1 Очень мило - это отличный способ сделать это без ViewState! Извините, я не заметил, что вам не нужен ViewState - в следующий раз я прочитаю вопрос более внимательно. - person Andrew Hare; 24.01.2009
comment
Спасибо за ваше первоначальное решение, оно действительно помогло пролить свет на порядок вещей. Думаю, я не так хорошо знаю жизненный цикл страницы asp.net, как думал. - person Dave Baghdanov; 24.01.2009