Customizando o Gridview

 

Bom, hoje vou falar um pouco sobre um controle muito usado em aplicações web o Gridview que na nova versão 2.0 do ASP.NET veio com algumas novidades bem legais.

 

Como eu sempre digo que ajudar ao próximo é uma ótima forma de você conhecer seus limites e até mesmo acabar com eles, sempre que posso eu tento contribuir no fórum do MSDN, onde eu pude perceber um numero considerável de usuários com dificuldades na customização e na implementação de algumas funcionalidades simples neste controle.

 

Então resolvi demonstrar como implementar algumas destas funcionalidades a seguir:

·         Customização do paginador;

·         Seleção de itens através de checkbox;

·         Mudança de cor das linhas selecionadas.

 

Pretendo com isso mostrar que é possível e que dependendo da customização também é simples. É importante dizer que as técnicas utilizadas neste artigo não são as únicas e tão pouco as mais eficientes até mesmo porque seria inverídico dizer isso sem um teste de desempenho ou algum outro teste que demonstrasse isso.

 

Para dar inicio ao nosso projeto crie um novo projeto Web Application no seu Visual Studio 2005 e renomei de MeuGridView. Feito isso vamos colocar a mão na massa.

 

O primeiro passo é criar o cenário da aplicação, que contem:

Um controle Gridview

Para listar os dados.

Um controle Button

Para recuperar os itens selecionados no gridview.

Um controle Label

Para exibir os itens selecionados no gridview.

Um objeto DataSource

Para carregar os dados a serem exibidos no gridview.

 

O objeto DataSource não tem muito que explicar ao menos nesta situação aonde vamos utilizá-lo apenas para carregar um GridView que não será editado. Então basta configurara a propriedade ConnectionString com a string de conexão e adicionar a nossa query na propriedade SelectQuery neste caso vamos utilizar uma query simples, como na Listagem 1.


SELECT id, nome, email FROM usuários

Listagem 1. Query que seleciona dados para carregar o objeto DataSource.

 

Eu estou utilizando uma tabela criada em um banco de dados que tenho para testes, vocês podem utilizar qualquer uma.

 

Com o objeto DataSource configurado vamos configurar o controle GridView, como na Listagem 2.

 

<asp:GridView ID="GridView1"

runat="server"

      DataSourceID="SqlDataSource1"

      AutoGenerateColumns="False"

      OnRowDataBound="GridView1_RowDataBound"

      AllowPaging="True"

      OnRowCreated="GridView1_RowCreated"

      PageSize="5"            

      DataKeyNames="email" OnRowCommand="GridView1_RowCommand">

      <AlternatingRowStyle BackColor="#F0F0F0" />

      <Columns>

      <asp:BoundField DataField="id" HeaderText="Id" />

      <asp:BoundField DataField="nome" HeaderText="Nome" />               

      <asp:BoundField DataField="email" HeaderText="E-mail" />               

      <asp:TemplateField>

      <ItemTemplate>

           <input type="checkbox" runat="server" id="chk" value='<%# Eval("id")%>' />

      </ItemTemplate>

      </asp:TemplateField>

      <asp:TemplateField>

            <ItemTemplate>

                  <a href="#" ID="lnk" runat="server">Detalhe</a>                       

            </ItemTemplate>

      </asp:TemplateField>               

      </Columns>                                 

      <PagerSettings PageButtonCount="4" />

      <PagerStyle HorizontalAlign="Right" />

      </asp:GridView>

Listagem 2. Configurando propriedades controle Gridview.

 

Como podem ver o GridView esta preparado para recuperar os dados do objeto DataSource que nós configuramos, assim como também esta configurado para não criar colunas automaticamente e para executar alguma rotina nos eventos onRowDataBound, onRowCreated e onRowCommand.

 

Já que defini que as colunas têm que ser criada manualmente foi o que fiz, criei as colunas baseado na minha fonte de dados, onde usei colunas do tipo BoundField somente serão exibidos os dados e onde eu coloquei TemplateField é porque vou usar um controle ou vou customizar a forma da coluna ser exibida. É possível perceber que existe uma coluna que vamos colocar um controle checkbox, alguns devem estar se perguntando por que eu não usei um webcontrol e sim um htmlcontrol, fiz isso porque o checkbox do ASP.NET não tem mais uma propriedade value onde neste caso vamos armazenar o id do item selecionado.

 

Um ponto importante que deve ficar claro é onde devemos usar as expressões de ligação e qual delas. Expressões de ligação no caso do GridView são Eval e Bind, ambas as expressões carregam dados da origem de dados em tempo de execução, porém a expressão Eval normalmente é usada em situações que os dados não serão atualizados (somente leitura), além de ter um segundo parâmetro opcional para formatar os caracteres obtidos na origem de dados. Já a expressão Bind é usada para exibir os dados em situações que necessitem de atualização, ou seja, em controle de entrada como o controle TextBox usado no modo de edição do GridView. Claro que nada impede de você usar a expressão Bind somente pra carregar os dados.

Agora que o GridView esta configurado vamos à implementação dos eventos.

Para customizar um GridView é extremamente importante saber como ele funciona por traz, isso significa que para manipular ele precisamos saber no que ele se transforma quando é apresentado no browser. Um GridView depois de carregado ele, assim como todos os webcontrols é renderizado, ou seja, é convertido para tags HTML o que significa que neste caso ele se transforma em uma table HTML. Sabendo disso já fica um pouco mais fácil planejar qualquer customização.

A Listagem 3 ilustra o código a ser inserido no evento onRowCreated.

 

if (e.Row.RowType == DataControlRowType.Header) {

     if (GridView1.AllowSorting) {
          foreach (TableCell celula in e.Row.Cells) {

if (celula.Controls.Count > 0 && celula.Controls[0] is LinkButton)

                    {

                        if (GridView1.SortExpression == ((LinkButton)celula.Controls[0]).CommandArgument)

                        {

                            string strImgOrdenar = string.Empty;

                            if (GridView1.SortDirection == SortDirection.Ascending)

                                strImgOrdenar = "~/images/sortascending.gif";

                            else

                                strImgOrdenar = "~/images/sortdescending.gif";

 

                            Image imgOrdem = new Image();

                            imgOrdem.ID = "imgOrdem";

                            imgOrdem.ImageUrl = strImgOrdenar;

                            celula.Controls.Add(imgOrdem);

                        }

                    }

                }

            }

        }

 

        if (e.Row.RowType == DataControlRowType.Pager)

        {

            Table tabela = ((Table)e.Row.Controls[0].Controls[0]);

            TableCell cell = new TableCell();

            LinkButton link = new LinkButton();

 

            //Link anterior

            link.ID = "lnkAnterior";

            link.Text = "<< Anterior";

            link.CommandName = "anterior";

            cell.Controls.Add(link);

            tabela.Rows[0].Cells.AddAt(0, cell);

 

            //Link próximo

            cell = new TableCell();

            link = new LinkButton();

            link.ID = "lnkProximo";

            link.CommandName = "proximo";

            link.Text = "Próximo>>";

            cell.Controls.Add(link);

            tabela.Rows[0].Cells.Add(cell);

        }

Listagem 3. Modifica o paginador e o header do GridView.

 

Vejam que a primeira coisa que é feito antes de implementar a modificação é identificar em que setor do GridView estamos, isso é possível através da propriedade RowType. Sabendo como identificar em que setor a leitura esta sendo feita naquele momento é possível minimizar a probabilidade de erros a e é possível focar as modificações.


Para inserir uma imagem ao lado da coluna selecionada para ordenar que ilustre de que forma foi ordenada vamos percorrer todas as células da linha do header do GridView, onde para cada célula desta linha vamos verificar se existe um controle e caso exista vamos testar se é um controle do tipo LinkButton, caso seja já sabemos que essa é uma coluna definida pra ser ordenada, agora vamos verificar se a coluna atual no loop é igual à coluna selecionada para ser ordenada, a Listagem 4 ilustra como é feita esta comparação.

 

if (GridView1.SortExpression == ((LinkButton)celula.Controls[0]).CommandArgument)

Listagem 4. Verifica se a coluna é a coluna selecionada pra ordenação.

 

Feita esta comparação vamos verificar que tipo de ordenação foi selecionada, o próprio GridView oferece uma propriedade que expoe esse tipo, a verificacao pode ser vista na Listagem 5.

 

if (GridView1.SortDirection == SortDirection.Ascending)

        strImgOrdenar = "~/images/sortascending.gif";

else

        strImgOrdenar = "~/images/sortdescending.gif";

 

Image imgOrdem = new Image();

imgOrdem.ID = "imgOrdem";

imgOrdem.ImageUrl = strImgOrdenar;

celula.Controls.Add(imgOrdem);

Listagem 5. Insere controle imagem na coluna ordenada.

 

A verificação do tipo de ordenacao é importante para definir qual imagem sera inserida.

 

Agora que já sabemos como tratar o controle GridView a Listagem 6 ilustra a customização do paginador.

 

        if (e.Row.RowType == DataControlRowType.Pager)

        {

            Table tabela = ((Table)e.Row.Controls[0].Controls[0]);

            TableCell cell = new TableCell();

            LinkButton link = new LinkButton();

 

            //Link anterior

            link.ID = "lnkAnterior";

            link.Text = "<< Anterior";

            link.CommandName = "anterior";

            cell.Controls.Add(link);

            tabela.Rows[0].Cells.AddAt(0, cell);

 

            //Link próximo

            cell = new TableCell();

            link = new LinkButton();

            link.ID = "lnkProximo";

            link.CommandName = "proximo";

            link.Text = "Próximo>>";

            cell.Controls.Add(link);

            tabela.Rows[0].Cells.Add(cell);

        }

Listagem 6. Insere controles linkbutton para customizar o paginador

 

Como podemos ver da mesma forma que inserimos as imagens no Header do GridView vamos inserir os controle LinkButton Próximo e Anterior que devem ter sua propriedade CommandArgument definida conforme sua função, assim como é mostrado no código pois essa propriedade será utilizada posteriormente.

 

A listagem 7 ilustra o código do evento onRowCommand.

 

        if (e.CommandName == "anterior")

        {

            if (GridView1.PageIndex > 0)

                GridView1.PageIndex -= 1;         

        }

 

        if (e.CommandName == "proximo")

        {

            if (GridView1.PageCount > (GridView1.PageIndex + 1))

                GridView1.PageIndex += 1;           

        }

Listagem 7. Código executado no click dos controles linkbutton Próximo e Anterior inseridos no evento onRowCreated.

 

Como foi dito agora vamos utilizar a propriedade CommandArgument que é passada ao evento onRowCommand quando um controle do tipo button e linkbutton no GridView é clicado.

 

A Listagem 8 ilustra o código do evento onRowDataBound, onde vamos inserir scripts para abrir popup passando informacoes para uma outra página, onde vamos mudar a cor do background da linha selecionada e onde vamos definir se os controles Próximo e Anterior serão exibidos.

 

       if (e.Row.RowType == DataControlRowType.DataRow) {

            System.Web.UI.HtmlControls.HtmlAnchor link = (System.Web.UI.HtmlControls.HtmlAnchor)e.Row.FindControl("lnk");

            link.Attributes.Add("onclick", "javascript:abrePopup('" + DataBinder.Eval(e.Row.DataItem, "id").ToString() + "');");

                       

            if (e.Row.FindControl("chk") != null)

                if (e.Row.RowState != DataControlRowState.Alternate)

                    ((System.Web.UI.HtmlControls.HtmlInputCheckBox)e.Row.FindControl("chk"))
  .Attributes.Add("onclick", "MudarBackground('" + ((System.Web.UI.HtmlControls.HtmlInputCheckBox)e.Row.FindControl("chk")).ClientID + "', 2);");

                else

                    ((System.Web.UI.HtmlControls.HtmlInputCheckBox)e.Row.FindControl("chk"))
  .Attributes.Add("onclick", "MudarBackground('" + ((System.Web.UI.HtmlControls.HtmlInputCheckBox)e.Row.FindControl("chk")).ClientID + "', 1);");

        }

 

        if (e.Row.RowType == DataControlRowType.Pager)

        {

            if(GridView1.PageIndex == 0){

                if (e.Row.FindControl("lnkAnterior") != null)

                    ((LinkButton)e.Row.FindControl("lnkAnterior")).Visible = false;

            }

 

            if ((GridView1.PageIndex) == (GridView1.PageCount -1))

            {

                if (e.Row.FindControl("lnkProximo") != null)

                    ((LinkButton)e.Row.FindControl("lnkProximo")).Visible = false;

            }

        }

Listagem 8. Código do evento onRowDataBound que trata linha a linha dos dados carregados no GridView.

 

Nesta etapa nada muda continuamos tendo os mesmo recursos que tinhamos no evento onRowCreated para identificar o setor onde esta sendo carregada a informacao no GridView.

 

O primeiro passo é identificar se a linha atual é do tipo Row, caso seja vamos procurar por um controle do tipo HtmlAnchor, já que no TemplateField colocamos um Link HTML que deve estar definido como Runat=”server” para que seja encontrado. Depois de localizado vamos inserir um atributo a ele, assim como mostra a Listagem 9.
 

System.Web.UI.HtmlControls.HtmlAnchor link = (System.Web.UI.HtmlControls.HtmlAnchor)e.Row.FindControl("lnk");

link.Attributes.Add("onclick", "javascript:abrePopup('" + DataBinder.Eval(e.Row.DataItem, "id").ToString() + "');");

Listagem 9. Insere atributo ao controle HTMLAnchor.

 

O parâmetro esperado neste caso pela função Javascript é o ID a ser passado para a nova página, mas como recuperar o id da linha atual no evento onRowDataBound ? Vamos utilizar a mesta expressão utilizada na configuração do GridView DataBinder.Eval(e.Row.DataItem, "id").ToString() essa expressão recupera a partir da linha do GridView o campo determinado.

 

Agora vamos localizar o controle Checkbox, assim como mostra a Listagem 10.

 

            if (e.Row.FindControl("chk") != null)

                if (e.Row.RowState != DataControlRowState.Alternate)

                    ((System.Web.UI.HtmlControls.HtmlInputCheckBox)e.Row.FindControl("chk"))
  .Attributes.Add("onclick", "MudarBackground('"

  ((System.Web.UI.HtmlControls.HtmlInputCheckBox)e.Row.FindControl("chk"))
  .ClientID + "', 2);");

                else

                    ((System.Web.UI.HtmlControls.HtmlInputCheckBox)e.Row.FindControl("chk"))
  .Attributes.Add("onclick", "MudarBackground('" +

  ((System.Web.UI.HtmlControls.HtmlInputCheckBox)e.Row.FindControl("chk"))
  .ClientID + "', 1);");

Listagem 10. Insere atributo ao controle Checkbox para modificar o background a linha selecionada.

 

A função javascript que é inserida neste controle checkbox espera dois parametros um que definie o ID do controle e outro que defini se é um controle que esta em uma linha com o AlternatingRowStyle definido e sua cor é diferente, isso é feito para que quando o controle for desmarcado a cor volte a ser a anterior.

 

A Listagem 11 mostra as funções JavaScript utilizadas neste artigo.

 

        function abrePopup(id)

        {           

            window.open('webform1.aspx?id=' + id, 'Detalhe','width=150,height=100,resizable=0,statusbar=0,scrollbars=0');

        }

       

        function MudarBackground(id, corOriginal)

        {

            var objeto = document.getElementById(id);

           

            if(objeto != null){              

                if(objeto.checked){

                    objeto.parentElement.parentElement.style.background = "#CCFFFF";

                }else{                   

                    if( (corOriginal % 2) > 0 )

                        objeto.parentElement.parentElement.style.background = "#F0F0F0";

                    else

                        objeto.parentElement.parentElement.style.background = "#FFFFFF";

                }

            }

        }

Listagem 11. Funçoes JavaScript que definem novo background da linha selecionada e que abrem nova página passando parâmetro.

 

E por fim vamos testar se os controles de paginação serão exibidos, a listagem 12 ilustra o código.

 

        if (e.Row.RowType == DataControlRowType.Pager)

        {

            if(GridView1.PageIndex == 0){

                if (e.Row.FindControl("lnkAnterior") != null)

                    ((LinkButton)e.Row.FindControl("lnkAnterior")).Visible = false;

            }

 

            if ((GridView1.PageIndex) == (GridView1.PageCount -1))

            {

                if (e.Row.FindControl("lnkProximo") != null)

                    ((LinkButton)e.Row.FindControl("lnkProximo")).Visible = false;

            }

        }

Listagem 12. Verifica se os controles serão exibidos.

 

Por fim a Listagem 13 ilustra como devem ser recuperados os ids das linhas selecionadas no evento click do Button.

 

        this.lblSelecionados.Text = "Itens selecionados:&nbsp;";

        foreach (string key in Request.Form.Keys) {

            if (key.EndsWith("chk")) {

                if (((System.Web.UI.HtmlControls.HtmlInputCheckBox)Page.FindControl(key)).Checked)

                    this.lblSelecionados.Text += Request.Form[key] + ", ";

            }

        }

Listagem 13. Código que recupera itens selecionados no GridView.

 

Como o PostBack é uma postagem da página sempre que ele é executado os os valores do controles também é submetido e por isso podem ser recuperar atraves da técnica descrita.


A Figura 1 ilustra o resultado obtido.

 

ddcgridvfig01.jpg
ddcgridvfig02.jpg

Figura 1. Resultado final.

 

E por hoje é só pessoal, espero ter sido claro nas explicações até mesmo por que os métodos utilizados forão escolhidos por sua simplicidade para facilitar o entendimento.