Динамически создаваемые элементы управления ASP.NET и обратная передача

Я знаю, что этот вопрос задавали тысячи раз, и я боролся с ним раньше, но по какой-то причине я не могу выполнить то, что я хочу выполнить... У меня есть динамически добавленный LinkButton, который при нажатии будет динамически добавлять элемент управления (в этом примере, текстовое поле) к той же панели. Цель состоит в том, чтобы постоянно добавлять столько элементов управления, сколько раз, когда был нажат LinkButton (т.е. Я нажму один раз, один флажок, затем другой щелчок даст мне 2 поля, другой щелчок добавит третий). В приведенном ниже коде я использую текущую дату и время, сериализованные для создания уникального идентификатора для каждого элемента управления текстовыми полями.

Когда я запускаю код, нажатие "Добавить фильтр" создаст новое текстовое поле, но после его повторного нажатия создаст новый и удалит его перед ним. Вместо этого я хочу сохранить предыдущий текстовый блок, а также любые данные, представленные в нем.

Ваша помощь приветствуется.

В aspx:

<asp:Panel ID="pnlFilter" runat="server">

</asp:Panel>

В aspx.cs:

protected void Page_Init(object sender, EventArgs e)
{
        LinkButton lb = new LinkButton();
        lb.ID = "lbAddFilter";
        pnlFilter.Controls.Add(lb);
        lb.Text = "Add Filter";
        lb.Click += new EventHandler(lbAddFilter_Click);
}


void lbAddFilter_Click(object sender, EventArgs e)
{
    TextBox tb = new TextBox();
    tb.ID = "tb" + DateTime.Now.ToBinary().ToString();
    pnlFilter.Controls.Add(tb);
}

Ответы

Ответ 1

Для других, кто пытается сделать что-то вроде этого: не делайте этого. Вместо этого подумайте о потоке информации и поймите, что есть лучший способ сделать это. Элементы управления вводами не должны создаваться динамически. Они могут быть статическими, а после заполнения и отправки на них эта информация должна куда-то (база данных, кеш, сеанс, например). После того, как он там, при обратной передаче, просмотрите все элементы в вашем хранилище и создайте для них отображение.

Вот что я сделал, и это облегчило жизнь. Надеюсь, это поможет кому-то.

Ответ 2

void lbAddFilter_Click(object sender, EventArgs e)
{
    TextBox tb = new TextBox();
    tb.ID = "tb" + DateTime.Now.ToBinary().ToString();
    pnlFilter.Controls.Add(tb);
}

Это не сработает - это проблема жизненного цикла, как вы предполагали в своем вопросе. Есть много способов, которыми вы можете работать с этим, но предложение Honus Wagner является самым гибким.

Что здесь происходит, так это то, что вы вызываете Controls.Add в обработчике событий, а элемент управления добавляется в список управления и в следующий запрос (будь то частичная обратная передача, полная обратная передача или новая страница), что контроль ушел, потому что вы его нигде не сохраняли.

Вот что происходит в жизненном цикле событий вашей страницы прямо сейчас:

  • Страница вызывает OnInit и добавляет ваш LinkButton
  • Пользователь нажимает кнопку LinkButton
  • Страница вызывает OnInit, чтобы начать запрос обратной передачи. LinkButton воссоздан
  • Вызывается обработчик событий, который динамически создает ваш TextBox и добавляет его в коллекцию управления.
  • Пользователь снова нажимает кнопку LinkButton.
  • Страница вызывает OnInit, чтобы снова добавить LinkButton. Коллекция pnlFilter.Controls пуста на этом этапе, за исключением тех элементов, которые вы указали статически в своей разметке.
  • Вызывается обработчик событий и добавляется новый TextBox в список управления.

Я не уверен, что это лучшая практика, но у меня был успех с подобной ей моделью (обратите внимание, что я не тестировал этот код - может потребоваться настройка):

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

    LinkButton lb = new LinkButton();
    lb.ID = "lbAddFilter";
    pnlFilter.Controls.Add(lb);
    lb.Text = "Add Filter";
    lb.Click += new EventHandler(lbAddFilter_Click);

    // regenerate dynamically created controls
    foreach ( var control in PersistedControls )
    {
        pnlFilter.Controls.Add(GenerateControl(control));
    }
}

private IList<Control> _persistedControls;
private const string PersistedControlsSessionKey = "thispage_persistedcontrols";
private IList<Control> PersistedControls
{
    if (_persistedControls == null)
    {
        if (Session[PersistedControlsSessionKey] == null)
            Session[PersistedControlsSessionKey] = new List<Control>();
        _persistedControls = Session[PersistedControlsSessionKey] as IList<Control>;
    }
    return _persistedControls;
}    

void lbAddFilter_Click(object sender, EventArgs e)
{
    TextBox tb = new TextBox();
    tb.ID = "tb" + DateTime.Now.ToBinary().ToString();
    PersistedControls.Add(tb);
    pnlFilter.Controls.Add(tb);
}

Обратите внимание, что я не уверен в добавлении фактических объектов Control в хранилище сеансов. Я считаю, что есть проблемы с идентификаторами управления, которые будут вызывать ошибки при обратной передаче (может быть, элемент управления не сериализуем?). В решении, которое я разработал, я создал новый TextBox/Label/ComboBox/etc в моем эквиваленте метода GenerateControl и сохранил пользовательский класс контейнера в коллекции PersistedControls, который содержит различные свойства элемента управления пользовательского интерфейса, которые мне нужно будет генерировать это на каждой странице Init.

Ответ 3

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

Я знаю, что контроллер Repeater хранит достаточную информацию о своих дочерних элементах, чтобы воссоздать их при привязке к базе данных... Возможно, вы сможете использовать его, чтобы сохранить небольшую работу.

Ответ 4

Попробуйте добавить

if(!Page.IsPostback)
  --- your code that adds different controls.