Controles adicionados dinamicamente no Asp.Net

Eu estou tentando envolver minha cabeça em torno do asp.net. Eu tenho um fundo como um desenvolvedor de php de longo tempo, mas agora estou enfrentando a tarefa de aprender asp.net e estou tendo alguns problemas com isso. Pode muito bem ser porque eu estou tentando forçar a estrutura em algo que não é destinado – então eu gostaria de aprender como fazer isso “da maneira certa”. 🙂

Meu problema é como adicionar controles a uma página por meio de programação em tempo de execução. Até onde eu sei, você precisa criar os controles em page_init, caso contrário eles desaparecem no próximo PostBack. Mas muitas vezes estou enfrentando o problema que não sei quais controles adicionar no page_init, pois depende dos valores do PostBack anterior.

Um cenário simples poderia ser um formulário com um controle suspenso adicionado no designer. A lista suspensa está definida como AutoPostBack. Quando o PostBack ocorre, preciso renderizar um ou mais controles denepending no valor selecionado no controle suspenso e, de preferência, esses controles devem agir como se tivessem sido adicionados pelo design (como em “quando postado de volta, comportar-se” corretamente “).

Eu estou indo pelo caminho errado aqui?

Eu concordo com os outros pontos feitos aqui “Se você pode sair da criação de controles dinamicamente, então faça isso …” (por Jesper Blad Jenson aka ), mas aqui está um truque que eu trabalhei com controles criados dinamicamente no passado.

O problema se torna galinha e ovo. Você precisa do seu ViewState para criar a tree de controle e precisa que sua tree de controle seja criada para chegar ao seu ViewState. Bem, isso está quase correto. Existe uma maneira de obter seus valores de ViewState antes que o restante da tree seja preenchido. Isso é substituindo LoadViewState(...) e SaveViewState(...) .

No SaveViewState, armazene o controle que você deseja criar:

 protected override object SaveViewState() { object[] myState = new object[2]; myState[0] = base.SaveViewState(); myState[1] = controlPickerDropDown.SelectedValue; return myState } 

Quando a estrutura chamar sua substituição “LoadViewState”, você receberá de volta o object exato retornado de “SaveViewState”:

 protected override void LoadViewState(object savedState) { object[] myState = (object[])savedState; // Here is the trick, use the value you saved here to create your control tree. CreateControlBasedOnDropDownValue(myState[1]); // Call the base method to ensure everything works correctly. base.LoadViewState(myState[0]); } 

Eu usei isso com êxito para criar páginas ASP.Net onde um DataSet foi serializado para o ViewState para armazenar alterações em uma grade inteira de dados, permitindo que o usuário faça várias edições com o PostBacks e, finalmente, confirme todas as alterações em um único “Salvar” Operação.

Você deve adicionar seu controle dentro do evento OnInit e o viewstate será preservado. Não use if (ispostback), pois controles devem ser adicionados toda vez, evento em postback!
(De) Serialização de viewstate acontece após OnInit e antes de OnLoad, portanto, seu provedor de persistência viewstate verá controles adicionados dinamicamente se eles forem incluídos em OnInit.

Mas no cenário que você está descrevendo, provavelmente multiview ou simples hide / show (propriedade visível) será a melhor solução.
É porque no evento OnInit, quando você precisa ler o menu suspenso e adicionar novos controles, o viewstate não é lido (desserializado) ainda e você não sabe o que o usuário escolheu! (você pode fazer request.form (), mas isso parece meio errado)

Depois de ter lutado com esse problema por um tempo, descobri essas regras básicas que parecem funcionar, mas YMMV.

  • Use controles declarativos sempre que possível
  • Use binding de dados sempre que possível
  • Entenda como o ViewState funciona
  • A propriedade Visibilidade pode percorrer um longo caminho
  • Se você deve usar adicionar controles em um manipulador de events, use a dica de Aydsman e recrie os controles em um LoadViewState substituído.

VERDADEMENTE Entender o ViewState é uma leitura obrigatória.

Noções básicas sobre controles dynamics pelo exemplo mostra algumas técnicas sobre como usar binding de dados em vez de controles dynamics.

VERDADEIRAmente Entender Controles Dinâmicos também esclarece técnicas que podem ser usadas para evitar controles dynamics.

Espero que isso ajude os outros com os mesmos problemas.

Se você realmente precisa usar controles dynamics, o seguinte deve funcionar:

  • No OnInit, recrie exatamente a mesma hierarquia de controle que estava na página quando a solicitação anterior foi atendida. (Se este não é o pedido inicial, claro)
  • Após OnInit, o framework carregará o viewstate do pedido anterior e todos os seus controles devem estar em um estado estável agora.
  • No OnLoad, remova os controles que não são necessários e adicione os necessários. Você também precisará salvar de alguma forma a tree de controle atual neste ponto, para ser usada na primeira etapa durante a solicitação a seguir. Você pode usar uma variável de session que dita como a tree de controle dinâmica foi criada. Eu mesmo armazenei toda a coleção Controls na session uma vez ( ponha de lado seus pitchforks, era apenas para uma demonstração ).

Adicionar novamente os controles “obsoletos” que você não precisará e será removido no OnLoad de qualquer maneira parece um pouco peculiar, mas o Asp.Net não foi realmente projetado com a criação de controle dynamic em mente. Se a mesma hierarquia de controle não for preservada durante o carregamento do viewstate, todos os tipos de erros difíceis de serem encontrados começarão a espreitar na página, porque os estados dos controles mais antigos são carregados nos recém-adicionados.

Leia sobre o ciclo de vida da página Asp.Net e especialmente sobre como o viewstate funciona e ficará claro.

Edit: Este é um artigo muito bom sobre como se comporta viewstate e o que você deve considerar ao lidar com controles dynamics: http://geekswithblogs.net/FrostRed/archive/2007/02/17/106547.aspx

Bem. Se você pode deixar de criar controles dinamicamente, então faça isso – caso contrário, o que eu devo fazer é usar Page_Load ao invés de Page_Init, mas ao invés de colocar coisas dentro do If Not IsPostBack, então configurei i diretamente no método.

Ah, esse é o problema com a abstração gotejante dos formulários da Web do ASP.NET.

Talvez você esteja interessado em olhar para a ASP.NET MVC, que foi usada para a criação deste site stackoverflow.com? Isso deve ser um ajuste mais fácil para você, vindo de um plano de fundo PHP (assim, pedal-para-o-metal quando se trata de HTML e Javascript).

Acho que a resposta aqui está no controle MultiView , de modo que, por exemplo, o menu suspenso alterna entre diferentes visualizações na visualização múltipla.

Você pode até mesmo vincular dados da propriedade de visualização atual da multiview ao valor da lista suspensa!

A única resposta correta foi dada por Aydsman. LoadViewState é o único local para adicionar controles dynamics onde seus valores de viewstate serão restaurados quando recriados e você pode acessar o viewstate para determinar quais controles adicionar.

Eu encontrei isso no livro “Pro ASP.NET 3.5 in C # 2008” sob a seção Criação de Controle Dinâmico:

Se você precisar recriar um controle várias vezes, você deve executar a criação de controle no manipulador de events Page.Load. Isso tem o benefício adicional de permitir que você use o estado de exibição com seu controle dynamic. Embora o estado de exibição seja normalmente restaurado antes do evento Page.Load, se você criar um controle no manipulador para o evento Page.Load, o ASP.NET aplicará as informações do estado de exibição após o término do manipulador de events Page.Load. Este processo é automático.

Eu não testei isso, mas você pode investigar isso.