msdn04_capa.JPG

Clique aqui para ler todos os artigos desta edição

 

Edição no Datagrid

por Marcos Santos e Renato Haddad

 

O DataGrid é um dos controles mais utilizados em ASP.NET e permite apresentar dados de forma clara, simples e de fácil manutenção. O objetivo deste artigo é mostrar como editar dados no DataGrid e capacitar o internauta a alterá-los por meio de controles existentes, paginação de dados, Bind dos controles e eventos, bem como a montar um DataGrid dinamicamente.

O primeiro passo é criar um projeto ASP.NET Web Application chamado DataGridEditable, usando como linguagem o Visual C# com um formulário denominado EditDataGrid.aspx. Insira um DataGrid chamado gridProdutos e formate-o de acordo com a sua necessidade. A princípio, altere apenas a propriedade AutoGenerateColumns para False.

Como as colunas não serão montadas automaticamente, é preciso informar quais colunas irão preencher o DataGrid. Para isso, pressione o botão direito no gridProdutos e selecione Property Builder. Na guia Columns, localize a lista Available Columns e selecione as seguintes colunas com as devidas propriedades:

 

Coluna

Header Text

Data Field

1 Bound Column

ProductID

ProductID

 

ReadOnly = True

 

1 Bound Column

ProductName

ProductName

1 Bound Column

UnitPrice

UnitPrice

 

ReadOnly = True

 

 

Data Formatting Expression = {0:c}

 

1 Template Column

CategoryID

 

1 Template Column

Discontinued

 

1 Button Column (tipo Edit, Update e Cancel)

 

 

 

Como o DataGrid exibe todos os dados listados nas colunas do Bound Column, é possível especificar que algumas colunas não sejam editadas quando a linha estiver no modo de edição. Neste exemplo, as colunas ProductID e UnitPrice não poderão ser alteradas, portanto, aplique a propriedade Read Only como True. Veja na Figura 1 as colunas selecionadas.

Figura 1 – Seleção das colunas do DataGrid

image002.jpg

 

Pressione o botão OK e note que o gridProdutos já contém as novas colunas customizadas. É possível perceber também que as colunas CategoryID e Discontinued não contêm nenhum tipo de controle.

Em nosso exemplo, usaremos o banco de dados Northwind, o que significa que na estrutura da tabela de produtos um produto pertencerá a uma determinada categoria. Quando o internauta editar o produto, o ideal é fazer com que ele selecione a categoria correspondente ao produto em um DropDownList. Para isso, precisamos criar uma coluna do tipo Template Column. A mesma analogia se aplica ao campo Discontinued, pois este campo é boleano. Com isso, iremos montar um Template Column com um CheckBox.

Para começar, pressione o botão direito no gridProdutos e selecione a opção: Edit Template / Columns[3] – CategoryID. O [3] significa que essa coluna é a quarta coluna do DataGrid. Arraste até a sessão ItemTemplate dois Labels nomeados, respectivamente, como lbCategoryDesc e lbCategoryID_busca. Para lbCategoryDesc, configure a propriedade Text como nulo; para lbCategoryID_busca, configure Visible como False. Com isso, o conteúdo não será exibido. Veja os templates na Figura 2.

O objetivo desses controles é exibir a descrição da categoria quando o gridProdutos não estiver sendo editado. O lbCategoryID_busca será usado para localizar a categoria selecionada do produto a ser editado. Para preencher esse controle, selecione a propriedade DataBindings e, na janela aberta, selecione Text na parte Bindable Properties e, em Custom Binding Expression, digite DataBinder.Eval(Container,"DataItem.CategoryID"). O DataBinder.Eval relacionará o CategoryID do DataSource ao componente lbCategoryID_busca.

A seguir, arraste um DropDownList até a sessão EditItemTemplate e nomeie (ID) como dropCategoryEdit. O objetivo desse controle é disponibilizar ao internauta todas as categorias em um DropDownList, facilitando a interface e protegendo-nos de eventuais erros de digitação (já que o internauta é obrigado a selecionar uma categoria existente).

 

image004.gif

Figura 2 – Template das colunas

 

Para configurar a coluna Discontinued, pressione o botão direito no gridProdutos e selecione EditTemplate / Columns[4] – Discontinued. Arraste dois checkBox até as sessões ItemTemplate e EditItemTemplate e nomeie-os como cbDiscontinuedIt e cbDiscontinuedEdit (um para cada sessão). Para cbDiscontinuedIt, defina a propriedade Enabled como False para que o internauta não possa editar sem estar no modo de edição. Para finalizar os checkBoxes, configure a propriedade DataBindings em ambos, selecionando Checked em Bindable Properties e a expressão DataBinder.Eval(Container,"DataItem.Discontinued") em Custom Binding Expression. Veja na Figura 3 estas configurações.

 

image006.jpg

Figura 3 – Configuração do Checked

 

Para sair da edição, clique em OK e, em seguida, pressione o botão direito sobre o gridProdutos e selecione End Template Editing. Note que o gridProdutos já contém os controles personalizados definidos no Template Columns, conforme a Figura 4.

Como a quantidade de dados a serem exibidos pode ser grande, o ideal é implementar uma paginação no gridProdutos; portanto, pressione o botão direito, selecione Property Builder e, na guia Paging, configure 10 registros por página, selecionando também a opção Allow paging e em Mode, selecione a opção Page numbers para que sejam mostrados números ao invés de setas. As cores da Figura 4 podem ser definidas, clicando com o botão direito do mouse e escolhendo a opção Auto Format.

 

image008.gif

Figura 4 – Template final do gridProdutos

 

Códigos

 

O funcionamento do formulário ocorre da seguinte forma: na primeira vez que o formulário for carregado, será executada uma rotina para montar o gridProdutos e armazenar todos os dados na Session. A vantagem desse modelo é que, além de não ser necessário ir ao banco de dados todas as vezes que a página for carregada, ele atualiza apenas a Session, e não o banco de dados em si.

Como usaremos o SQL Server como exemplo e precisaremos concatenar Strings, digite na lista Using as seguintes classes:

 

using System.Data.SqlClient;

using System.Text;

 

Para definir a string de conexão com o banco de dados digite na lista Protected o código referente à sua conexão. Cabe ressaltar que essa conexão deve ser configurada de acordo com a instalação do SQL Server no servidor, o que pode incluir Password, segurança integrada, etc.

 

protected const string conexao = "database=northwind;server=(local);user id=sa";

 

No momento da edição do gridProdutos, é preciso verificar qual a categoria atual do produto a ser editado, a fim de que, quando for exibida a lista de categorias no DropDownList, essa categoria já apareça selecionada. Portanto, declare a string categoryId_busca na lista protected.

 

// categoryId_busca utilizado posteriormente no ItemDataBound

protected string categoryId_busca = "";

 

Digite o código a seguir no evento Page_Load para montar o gridProdutos. Todos os códigos contidos no Page.IsPostBack serão executados apenas na primeira vez em que página for carregada.

 

private void Page_Load(object sender, System.EventArgs e)

{

       if (!Page.IsPostBack)

       {

             montaGrid();

       }

}

 

Digite a rotina montaGrid, que seleciona todos os produtos da tabela Products, monta um DataTable e armazena a tabela na Session. Ao final, ela define a origem e preenche o gridProdutos.

 

//----Este evento será invocado toda vez que um PostBack tiver origem no próprio DataGrid------//

private void montaGrid()

{

       // Na primeira vez que a página for carregada

       // não haverá a Session "dtProducts"

       DataTable dtProducts = (DataTable)Session["dtProducts"];

       if (dtProducts == null)

       {

             // Define o Sql que retorna os

// produtos e as categorias

             StringBuilder sql = new StringBuilder();

             sql.Append("Select ProductID, ProductName, UnitPrice, CategoryID,");

             sql.Append(" Discontinued from Products");

             sql.Append(" order by productId");

 

             // Conecta ao Banco e cria um DataSet (ds)

             // com o resultado da consulta

             SqlConnection conn = new SqlConnection(conexao);

             SqlDataAdapter da = new SqlDataAdapter(sql.ToString(),conn);

             DataSet ds = new DataSet();

             da.Fill(ds,"Products");

 

             dtProducts = ds.Tables["Products"];

 

             // Armazena DataTable "Products" em Session

             // para que seja atualizado no evento de edição

             Session["dtProducts"] = dtProducts;

       }

       gridProdutos.DataSource = dtProducts;

       gridProdutos.DataBind();

}

 

Observe que os dados já estão sendo exibidos no gridProdutos, mas se o internauta quiser alterar o conteúdo de um determinado produto, ele precisará selecionar o botão Edit. Usando programação, precisamos interceptar esse evento e criar o código para exibir os dados atuais e disponibilizar as colunas editáveis para possíveis alterações. Selecione o gridProdutos, pressione F4 para exibir a janela de propriedades, clique no raio (Events), localize o evento EditCommand e dê um duplo clique. Digite o seguinte código:

 

// Evento ao clicar no botão Edit

private void gridProdutos_EditCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e)

{

       // fornece ao grid o item a ser editado

       gridProdutos.EditItemIndex = e.Item.ItemIndex;

       this.categoryId_busca = ((System.Web.UI.WebControls.Label)e.Item.FindControl("lbCategoryID_busca")).Text;

       // o componente lbCategoryID_busca é visible=false,

       // para capturar o código a ser selecionado no lookup.

       montaGrid();

}

 

A variável categoryId_busca armazena o conteúdo do CategoryID referente ao produto editado. Isso é necessário para que, quando o internauta selecionar outra categoria no DropDownList, a categoria atual já apareça selecionada.

Em seguida, é preciso criar o evento ItemDataBound para preencher o controle Label. Esse evento representa a última oportunidade para se acessar o dado de um item (linha) antes que ele seja exposto ao usuário. Portanto, nos itens de visualização, é invocado o método retornaDescricaoCategory, que pega o CategoryId armazenado no Label lbCategoryID_busca existente no ItemTemplate da coluna CategoryID e retorna a descrição para o Label lbCategoryDesc visível ao usuário.

No Item em que está sendo requisitado o modo de edição, o DropDownList é preenchido com os registros da tabela Categories e a categoria atual já está selecionada, esta já conhecida pelo armazenamento no atributo this.categoryId_busca. Veja a Listagem 1.

Para criar este evento, selecione o gridProdutos e, na janela de propriedades, dê um clique duplo no evento ItemDataBound.

Listagem 1 – Evento ItemDataBound do gridProdutos

private void gridProdutos_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)

{

       // Verifica o Item a ser Editado

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

       {

             System.Web.UI.WebControls.DropDownList lookup = (System.Web.UI.WebControls.DropDownList)e.Item.FindControl("dropCategoryEdit");

             if (lookup != null)

             {

                    lookup.DataSource = this.retornaDataTable_Category();

                    lookup.DataTextField = "CategoryName";

                    lookup.DataValueField = "CategoryID";

                    lookup.DataBind();

                    //Já seleciona a Categoria atual

                    lookup.SelectedValue = lookup.Items.FindByValue(this.categoryId_busca).Value;

             }

       }

       else //modo de Visualização

       {                  

             System.Web.UI.WebControls.Label lbCategoryID = (System.Web.UI.WebControls.Label)e.Item.FindControl("lbCategoryID_busca");

             System.Web.UI.WebControls.Label lbCategoryDesc = (System.Web.UI.WebControls.Label)e.Item.FindControl("lbCategoryDesc");

             if ((lbCategoryID != null) && (lbCategoryDesc != null))

                    lbCategoryDesc.Text = this.retornaDescricaoCategory(lbCategoryID.Text);

       }

}

 

Veja a explicação detalhada das principais linhas do código da Listagem 1:

 

Verifica se está no modo de edição ou visualização.

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

 

Pesquisa o controle dropCategoryEdit no gridProdutos para saber se ele existe ou não.

System.Web.UI.WebControls.DropDownList lookup = (System.Web.UI.WebControls.DropDownList)e.Item.FindControl("dropCategoryEdit");

 

Se não existir, é porque está no modo de visualização de dados; caso contrário, será edição.

if (lookup != null)

 

Define a origem dos dados, ou seja, chama a rotina retornaDataTable_Category que retorna a lista de categorias existentes na tabela Categories.

lookup.DataSource = this.retornaDataTable_Category();

 

Define o que será exibido (DataTextField) e o que será armazenado (DataValueField). O DataBind preenche o respectivo controle.

lookup.DataTextField = "CategoryName";

lookup.DataValueField = "CategoryID";

lookup.DataBind();

 

Este código já seleciona a categoria atual do produto, afinal, o internauta precisa saber qual é a categoria atual para poder selecionar a nova categoria.

lookup.SelectedValue = lookup.Items.FindByValue(this.categoryId_busca).Value;

 

Caso seja apenas para visualização, pesquisa os controles que irão exibir a descrição da categoria oriunda da rotina retornaDescricaoCategory.

else

{                  

System.Web.UI.WebControls.Label lbCategoryID = (System.Web.UI.WebControls.Label)e.Item.FindControl("lbCategoryID_busca");

System.Web.UI.WebControls.Label lbCategoryDesc = (System.Web.UI.WebControls.Label)e.Item.FindControl("lbCategoryDesc");

if ((lbCategoryID != null) && (lbCategoryDesc != null))

lbCategoryDesc.Text = this.retornaDescricaoCategory(lbCategoryID.Text);

 

Como a descrição das categorias está armazenada na tabela Categories do banco de dados, para evitar que o banco de dados seja lido a cada item, é mais rápido armazenar toda a lista de categorias na Session dtCategory. A rotina a seguir será invocada a partir do ItemDataBound, tanto na edição como na visualização.

 

private DataTable retornaDataTable_Category()

{

       if (Session["dtCategory"] == null)

       {

             SqlConnection conn = new SqlConnection(conexao);

             SqlDataAdapter da = new SqlDataAdapter("select CategoryID, CategoryName from Categories order by CategoryName",conn);

             DataSet ds = new DataSet();

             da.Fill(ds,"Category");

             return ds.Tables["Category"];

       }

       else

             return (DataTable)Session["dtCategory"];

}

 

No código de visualização do ItemDataBound, para exibir a descrição da respectiva categoria, é preciso criar o método retornaDescricaoCategory para cada item do DataGrid.

 

private string retornaDescricaoCategory(object categoryID)

{

       string categ = categoryID.ToString();

       DataTable dt = retornaDataTable_Category();

       DataRow[] dr = dt.Select("CategoryID = "+categ);

       return dr[0]["CategoryName"].ToString();

}

 

Se o internauta estiver editando um item no gridProdutos e resolver cancelar a edição, bastará clicar no botão Cancel. No entanto, é preciso criar o código para o evento CancelCommand. Este código atribui -1 para o EditItemIndex e chama a rotina montaGrid.

 

private void gridProdutos_CancelCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e)

{

       gridProdutos.EditItemIndex = -1;

       montaGrid();

}

 

Se o internauta resolver salvar as alterações, será preciso criar o código correspondente para salvar os dados na Session e atualizar o gridProdutos. Quando executar este código, você perceberá que a categoria precisará ser selecionada em um DropDownList para que proporcione uma interface funcional para o gridProdutos. Note ainda que nem todos os campos são passíveis de alteração, pois são somente Read-Only.

Neste exemplo, é importante ressaltar que o código do UpdateCommand atualiza a Session. Caso queira atualizar diretamente o banco de dados, basta criar um Command com a T-SQL Update.

 

private void gridProdutos_UpdateCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e)

{

       // Altera os dados por Referência. Não precisa salvar novamente na sessão

       DataTable dt = (DataTable)Session["dtProducts"];

       if (dt != null)

       {

             // Pega o índice do DataTable que foi alterado, vindo do DataGrid

             int indice = e.Item.ItemIndex + (gridProdutos.CurrentPageIndex)*10;

             // TextBox criado automaticamente com o nome do produto alterado

             System.Web.UI.WebControls.TextBox txtProdName = (System.Web.UI.WebControls.TextBox)(e.Item.Cells[1].Controls[0]);

             System.Web.UI.WebControls.DropDownList lookup = (System.Web.UI.WebControls.DropDownList)e.Item.FindControl("dropCategoryEdit");

             System.Web.UI.WebControls.CheckBox cbDiscontinued = (System.Web.UI.WebControls.CheckBox)e.Item.FindControl("cbDiscontinuedEdit");

            

             // CategoryID, selecionado no DropDownList no modo de edição

             int categoryID = Convert.ToInt32(lookup.SelectedValue);

            

             // Não precisa salvar novamente a session. É automatico

             dt.Rows[indice]["ProductName"] = txtProdName.Text;

             dt.Rows[indice]["CategoryID"] = categoryID;

             dt.Rows[indice]["Discontinued"] = cbDiscontinued.Checked;

       }

       gridProdutos.EditItemIndex = -1;

       montaGrid();

}

 

Para finalizar a customização do gripProdutos, resta-nos implementar o evento para provocar a paginação. Portanto, crie o código a seguir no evento PageIndexChanged, que capturará o número da página selecionada e montará o gridProdutos para que exiba os produtos da página correspondente.

 

private void gridProdutos_PageIndexChanged(object source, System.Web.UI.WebControls.DataGridPageChangedEventArgs e)

{

       gridProdutos.CurrentPageIndex = e.NewPageIndex;

       montaGrid();

}

 

Salve o projeto no Solution Explorer, defina-o no Set As Start Page e execute (CTRL + F5) para ver o resultado do gridProdutos montado. Experimente editar algum produto (por exemplo, troque o nome, a categoria e os demais dados). Navegue nas páginas e note que as alterações são mantidas sempre na Session. Uma constatação: trabalhar com a fonte de dados desconectada permite uma excelente performance.

 

image010.jpg

Conclusões

Trabalhar com DataGrids pode deixar a sua aplicação robusta e fornecer uma interface profissional e de fácil entendimento aos internautas. Explore os recursos do DataGrid, os eventos e as facilidades que ele proporciona. “No stress, think .NET”.

 

Referências:

http://www.asp.net

http://www.gotdotnet.com

http://www.linhadecodigo.com.br