ASP.NET controles criados dinamicamente e Postback

Eu sei que essa pergunta foi feita milhares de vezes, e eu já lutei com isso antes, mas por algum motivo, não consigo realizar o que quero realizar … Eu tenho um LinkButton adicionado dinamicamente que, quando clicado, adiciona dinamicamente um controle (neste exemplo, uma checkbox de texto) para o mesmo painel. A intenção é adicionar continuamente tantos controles quantas vezes o LinkButton foi clicado (ou seja, eu clico nele uma vez, uma checkbox, então outro clique me dará 2 checkboxs, outro clique adiciona 3). No código abaixo, uso a data e a hora atuais serializadas para criar um ID exclusivo para cada controle de checkbox de texto.

Quando eu executo o código, clicar em “Adicionar Filtro” gerará uma nova checkbox de texto, mas, uma vez clicada novamente, criará uma nova e descartará a anterior. Em vez disso, quero persistir a checkbox de texto anterior, bem como todos os dados enviados nela.

Sua ajuda é apreciada.

No aspx:

  

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

Para qualquer outra pessoa tentando fazer algo assim: não. Em vez disso, pense no stream de informações e entenda que existe uma maneira melhor de fazê-lo. Os controles de input não precisam ser criados dinamicamente. Eles podem ser estáticos e, ao preencher e enviar em um, essa informação tem que ir em algum lugar (database, cache, session, por exemplo). Uma vez lá, no postback, percorra todos os itens em seu armazenamento de escolha e crie um display para eles.

Isso é o que eu fiz e tornou a vida muito mais fácil. Espero que ajude alguém.

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

Isso não vai funcionar – é um problema de ciclo de vida, como você inferiu na sua pergunta. Há muitas maneiras de trabalhar com isso, mas a sugestão de Honus Wagner é a mais flexível.

O que está acontecendo aqui é que você está chamando Controls.Add no manipulador de events e o controle está sendo adicionado à lista de controle e, na próxima solicitação (seja um postback parcial, um postback completo ou um novo hit de página), esse controle se foi porque você não persistiu em nenhum lugar.

Aqui está o que está acontecendo no ciclo de vida do evento de sua página agora:

  1. Chamadas de página OnInit e adiciona seu LinkButton
  2. O usuário clica no LinkButton
  3. Chamadas de página OnInit para iniciar a solicitação de postback. O LinkButton é recriado
  4. O manipulador de events é chamado, que cria dinamicamente seu TextBox e o adiciona à coleção de controle
  5. O usuário clica no LinkButton novamente
  6. Chamadas de página OnInit para adicionar o LinkButton novamente. A coleção pnlFilter.Controls está vazia neste momento, exceto pelos elementos que você declarou estaticamente na sua marcação.
  7. O manipulador de events é chamado e um novo TextBox é adicionado à lista de controle.

Não tenho certeza se essa é a melhor prática, mas obtive sucesso com um modelo semelhante a esse (note que não testei esse código – pode ser necessário fazer ajustes):

 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 _persistedControls; private const string PersistedControlsSessionKey = "thispage_persistedcontrols"; private IList PersistedControls { if (_persistedControls == null) { if (Session[PersistedControlsSessionKey] == null) Session[PersistedControlsSessionKey] = new List(); _persistedControls = Session[PersistedControlsSessionKey] as IList; } 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); } 

Observe que não tenho certeza sobre a adição de objects de controle reais ao armazenamento de session. Acredito que há problemas com as identificações de controle que causarão erros no postback (talvez um controle não seja serializável?). Na solução que desenvolvi, criei um TextBox / Label / ComboBox / etc fresco no meu equivalente do método GenerateControl e armazenei uma class de contêiner personalizada na coleção PersistedControls que continha as várias propriedades do controle de interface do usuário que precisaria gerar em cada página Init.

Eu acredito que você precisará recriar cada controle em cada postback.

Eu sei que o controle Repeater armazena informações suficientes sobre seus filhos para recriá-los quando databound … Você pode usá-lo para economizar um pouco de trabalho.

Tente adicionar

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