Utilizando DropDownLists múltiplos em um DataGrid

 

Neste artigo, iremos demonstrar como exibir e atualizar registros mestre múltiplos em um DataGrid, em que cada linha contém um DropDownList vinculado. A razão para termos escrito este artigo, foi a necessidade de atualizar registros de eventos múltiplos ao mesmo tempo, utilizando um DropDownList que contém todos os códigos de estado disponíveis para cada evento.

O método DataGrid standard, que provê um Button Edit para cada linha, mostrou-se ineficiente para a aplicação, e depois de procurar por exemplos nos sites de código habituais, desenvolvemos a essência da solução detalhada aqui. Usamos o banco de dados Northwind para demonstrar a técnica.

Exibindo os Registros

A Figura 1 mostra cinco registros da tabela Products do banco de dados Northwind, com o DropDownList povoado com os itens da tabela Categories. A category que é designada no momento para cada produto, é o item ‘selecionado ' no DropDownList.

 

Figura 1.

Povoando o DataGrid

O DataGrid é construído utilizando o TemplateColumn genérico e a sintaxe de ItemTemplate, na página aspx.

 

 

A coluna DropDownList utiliza o código do método dgProducts_ItemDataBound, que é um evento disparado para cada linha do DataGrid. Especificamos qual método disparar no atributo OnItemDataBound do DataGrid (dgProducts neste exemplo). A linha é pesquisada até que o controle DropDownList ‘productcategories’ for encontrado. Uma vez que o controle for obtido, será atribuído ao dataset Categories, ‘dsCategories’ anteriormente recuperado. O item selecionado é então configurado, utilizando o CategoryID recuperado da tabela Products.

 

public void dgProducts_ItemDataBound(object sender,

           System.Web.UI.WebControls.DataGridItemEventArgs e)

{

 int ColumnForCategoryID = 2; //Used to locate the data element we want

 

 if ((e.Item.ItemType == ListItemType.Item) ||

     (e.Item.ItemType == ListItemType.AlternatingItem))

 {   

  //Este eh o tipo de item que desejamos processar.

  //Percorrer os controles aa procura deste item.

  for (int i = 0; i < e.Item.Controls.Count; i++)

  {

   try

   {

    //Para este demo estamos preocupados apenas com o DropDownList que estah na linha

    if (e.Item.Controls[i].Controls[1].GetType().ToString() ==

               "System.Web.UI.WebControls.DropDownList")

    {

     System.Web.UI.WebControls.DropDownList ddl =

       (System.Web.UI.WebControls.DropDownList)

       e.Item.Controls[i].Controls[1];

     //Confirmar que o DropDownList eh o desejado.

     //O nome corresponde ao ID usado

     //no asp:DropDownList do ItemTemplate

     if (ddl.ID.ToString().ToLower().Equals("productcategories")) 

     {

      //Construir o DropDownList com as categories

      //obtidas com o metodo Page_Load

      ddl.DataSource     = dsCategories;

      ddl.DataTextField  = "CategoryName";

      ddl.DataValueField = "CategoryID";

      ddl.DataBind();

      //Configurar o item selecionado no DropDownList para a category

      //atualmente designada para este product em particular.

      //(Ver a declaração SQL no GetProductInfo

      // para saber quais colunas estao sendo recuperadas.)

      SetStatusDDLSelection(ddl, ((DataRowView)

        e.Item.DataItem).Row.ItemArray[ColumnForCategoryID].ToString());

     } //fim do if productcategories

    } // fim do if DropDownList

   } //fim do try

   catch (Exception ex)

   {

    //Fazer aquí algo melhor.

    lblMessageOut.Text += "
error=" + ex.Message  + "
";

   } //fim do catch

  }  //fim da repeticao do Controls.Count

 }   //fim do if ItemType

 

} //fim do dgProducts_ItemDataBound

 

Ver a ajuda do Microsoft.NET Framework Class library, para mais informações sobre o evento DataGrid.ItemDataBound. Uma outra coluna notável do DataGrid, é o campo RecordKeys. Este é um campo oculto do DataGrid, no qual é armazenada a chave primária do registro product (ProductID), assim como a chave estrangeira da tabela Categories (CategoryID), da category original.

 

Este mecanismo provê um modo fácil de recuperação de chaves do banco de dados para cada linha dados. Estas chaves serão usadas durante o processo de atualização, para determinar se um determinado registro deve ser atualização ou não. Mudamos o atributo Visible="False" no campo RecordKeys do ItemTemplate, para exibir a coluna da chave do registro. Notar que há muitos modos de salvar dados na grade ou no formulário; este é apenas um deles!

Seleções de variáveis

A título de exemplo, mudar a category do produto Aniseed Syrup, para passar a ser o Dairy Products e o Camembert Pierrot para passar a ser Condiments, tal como mostrado na Figura 2, antes de clicar no Button Update.

 

 

Figura 2.

Quando a página se posta para si mesma no evento on click do botão, o código no método Page_Load fluirá para o método DoTheWork onde o procedimento principal é realizado.

Fazendo o Trabalho

O propósito do método DoTheWork é de percorrer o DataGrid, processando cada DataGridItem existente. Para cada um dos itens DataGridItem, os controles que interessam - productcategories DropDownList, recordkeys Label e o productname Label - serão obtidos. O trecho de código a seguir, fornece uma noção de alto nível do método inteiro. Cada uma das áreas de código primárias será examinada separadamente.

 

private void DoTheWork()

{

 #region Create the local variables

 ...

 #endregion

 

 foreach (System.Web.UI.WebControls.DataGridItem dgi in dgProducts.Items)

 {

  ...

 

  #region Look for the DropDownList productcategories

  ...

  #endregion

 

  #region Look for the Label recordkeys

  ...

  #endregion

 

  #region Look for the Label productname

  ...

  #endregion

 

  #region Update Decision

  ...

  #endregion

 

  lblMessageOut.Text += LocalDisplayText.ToString();

 } //fim da repeticao do DataGridItem

 

} //fim do DoTheWork

 

Percorrendo o DataGrid

O laço foreach percorre todas as linhas do DataGrid, utilizando a propriedade Items, retornando uma coleção de DataGridItems.

 

foreach (System.Web.UI.WebControls.DataGridItem dgi in dgProducts.Items)

{

 ...

} // fim da repeticao DataGridItem

 

Cada um dos DataGridItems, será verificado para os controles específicos desejados. O DropDownList ‘productcategories’, será inicialmente localizado utilizando a propriedade FindControl. Caso achado, será feito o cast para um controle DropDownList. Neste momento, o SelectedValue é extraído do controle.

 

#region Look for the DropDownList productcategories

System.Web.UI.Control ProductCategoryControl =

                dgi.FindControl("productcategories");

if (!(ProductCategoryControl == null))

{

 if (ProductCategoryControl.GetType().ToString() ==

     "System.Web.UI.WebControls.DropDownList")

 {

  DropDownList ddl = (System.Web.UI.WebControls.DropDownList)

                                      ProductCategoryControl;

  CurrentCategoryKey.Length = 0;

  CurrentCategoryKey.Append(ddl.SelectedValue);

 }

}

ProductCategoryControl = null;

#endregion

 

Quando o DataGrid estava sendo povoado, as chaves product e category originais foram salvas no campo 'recordkeys', o qual será recuperado agora. O procedimento é o mesmo que o do DropDownList, só que agora será obtido um controle Label. Uma vez que o valor for achado, será salvo em um StringBuilder. Este procedimento será repetido para o controle ‘productname'.

 

#region Look for the Label recordkeys

System.Web.UI.Control RecordKeysControl = dgi.FindControl("recordkeys");

if (!(RecordKeysControl == null))

{

 if (RecordKeysControl.GetType().ToString() ==

     "System.Web.UI.WebControls.Label")

 {

  Label lbl = (System.Web.UI.WebControls.Label) RecordKeysControl;

  RecordInitialKeys.Length = 0;

  RecordInitialKeys.Append(lbl.Text.Trim());

  lbl = null;

 }

}

RecordKeysControl = null;

#endregion

 

Uma vez que os controles desejados foram processados para cada DataGridItem, uma verificação é feita para determinar se o registro mestre deverá ser atualizado, comparando o valor chave original de Category com o valor de Category atualmente selecionado. Para fazer isto, os dados de RecordInitialKeys são separados nos campos PreviousProductKey e PreviousCategoryKey. O PreviousCategoryKey é comparado ao CurrentCategoryKey. Se for diferente, então este registro Product deverá ser atualizado. O PreviousProductKey é utilizado para facilitar a atualização.

 

#region Update Decision

//Verificar se existem componentes para os quais eh preciso determinar

//se houve alteracao na selecao do DropDownList.

if ((RecordInitialKeys.Length > 0) && (CurrentCategoryKey.Length > 0))

{

 PreviousProductKey.Length = 0;

 PreviousCategoryKey.Length = 0;

 //Obter as chaves salvas do datagrid.

 //product key

 PreviousProductKey.Append(RecordInitialKeys.ToString().Split('|')[0]);

 //category key

 PreviousCategoryKey.Append(RecordInitialKeys.ToString().Split('|')[1]);

 LocalDisplayText.Append("
   " +

            ProductName.ToString() + " - ");

 if (!(PreviousCategoryKey.ToString().Trim().Equals(

                 CurrentCategoryKey.ToString().Trim())))

 {

  //A selecao mudou, ejecutar o procedimento de atualizacao.

  LocalDisplayText.Append("Update required!!!");   

  //Se alguna coisa foi feita eh melhor voltar para

  //esta pagina para que o controle DropDownList possa ser novamente populado

  //usando os valores atualizados do banco de dados!

 }

 else

 {

  LocalDisplayText.Append("The selection did not change.");   

 }

}

#endregion

Os Resultados

A Figura 3 mostra os resultados de alterar as categories de Aniseed Syrup e Camembert Pierrot.

 

Figura 3.

Conclusão

Apesar de que todas as técnicas mostradas aqui não são novas, esperamos ter apresentado um método para economizar tempo quando se apresentar uma situação semelhante.

Importante!

O código de exemplo mostrado aqui, não contém a maioria do processamento try/catch necessário, que seria inserido antes do mesmo ser considerado apto para produção! Precisaremos também monitorar o que é colocado no viewstate.