Desenvolvimento - ASP. NET

ASP.NET: Criando grupos e totalizações no GridView com apenas 2 linhas de código

O GridView, sem dúvida alguma, representa um grande avanço frente ao DataGrid do ASP.Net 1.1. Porém nossos clientes e usuários sempre necessitam de funcionalidades que vão além das fornecidas nativamente pelo GridView. Um exemplo frequente é a criação de totalizações e agrupamentos.

por Agrinei Sousa



O GridView, sem dúvida alguma, representa um grande avanço frente ao DataGrid do ASP.Net 1.1. Porém nossos clientes e usuários sempre necessitam de funcionalidades que vão além das fornecidas nativamente pelo GridView. Um exemplo frequente é a criação de totalizações e agrupamentos. No primeiro caso é relativamente fácil e rápido codificar o evento RowDataBound para obtenção de totais. A criação de grupos no GridView envolve um pouco mais de implementação e depuração. Há dezenas de artigos orientando tais implementações, porém eles induzem os desenvolvedores menos avisados à má prática da recodificação ao invés do reuso.

Mas é apenas quando precisamos combinar os dois recursos, grupos e sumarizações por grupo, que vemos o quanto é fácil se perder em código e depuração, caso não haja uma implementação consistente. Com o objetivo de simplificar essas tarefas desenvolvi o GridViewHelper, que permite a utilização rápida e confiável de tais recursos.

[Ver exemplo online]

[Baixar código fonte]

Uso do GridViewHelper

Veremos abaixo alguns exemplo de uso do GridViewHelper. Primeiramente exibimos o grid de referência para o qual serão criados os grupos e totalizações. Os dados dos exemplos são do banco Northwind:

Mais opções de agrupamento

É interessate mostrar mais dois exemplos de agrupamento. O primeiro apresenta um grupo composto por duas colunas. O segundo define um grupo de supressão, que tem um comportamento semelhante ao da cláusula group by do SQL. Ele suprime os valores repetidos no grupo, realizando uma operação de sumarização nas demais colunas.

Abaixo vemos a listagem e a aparência do grid com grupo composto:

protectedvoidPage_Load(objectsender,EventArgse)
{
GridViewHelper helper
=newGridViewHelper(this.GridView1);
string
[]cols=newstring[2];
cols[0]="ShipRegion";
cols[1]="ShipName";
helper.RegisterGroup(cols,true,true);
helper.ApplyGroupSort();
}


Perceba o que ocorre com o sumário em destaque. O sumário do grupo é criado depois do cabeçalho do grupo SP. Isso ocorre pois a sequência de eventos é:

Group1_Start
Group1_End
Group2_Start
Group2_End

E para um funcionamento adequado a sequência deveria ser:

Group1_Start
Group2_Start
Group2_End
Group1_End

Implementação

Na implementação do GridViewHelper optei por definir uma classe independente para que as funcionalidades pudessem ser utilizadas com qualquer GridView, ao invés de criar um controle customizado herdado de GridView. Essa opção mostra-se adequada por não "amarrar" o desenvolvedor a nenhuma classe base estranha ao seu projeto. Além da classe GridViewHelper há mais duas classes auxiliares, GridViewSummary e GridViewGroup, que representam os elementos sumário e grupo.

Na criação do GridViewHelper é mantida uma referência do GridView e também associa-se o evento RowDataBound do GridView para o método do GridViewHelper responsável pelo trabalho:

publicGridViewHelper(GridViewgrd,booluseFooterForGeneralSummaries,SortDirectiongroupSortDirection)
{
this.mGrid=grd;
this
.useFooter=useFooterForGeneralSummaries;
this
.groupSortDir=groupSortDirection;
this
.mGeneralSummaries=newList<GridViewSummary>();
this
.mGroups=newList<GridViewGroup>();
this
.mGrid.RowDataBound+=newGridViewRowEventHandler(RowDataBoundHandler);
}


O constructor listado é o que possui todos os parâmetros, incluindo a opção de sentido de ordenação. Como vemos, o método RowDataBoundHandler é o responsável por processar o evento do grid e fazer todo o trabalho para geração de grupos e sumários.

Alguns métodos utilizados internamente no GridViewHelper foram mantidos públicos pois provêem algumas funcionalidades interessantes que podem precisar ser acessadas externamente para alguma customização visual ou funcional.

Aspectos relevantes

A performance das operações deve ser prejudicada em função do "uso abusivo" de boxing e unboxing de value types.Uma possibilidade para tentar minimizar esse problema é implementar as operações nativas do GridViewHelper com Generics, mas isso não é trivial como seria desejável, como podemos ver em Using Generics for Calculations. Outra possibilidade pode ser vista em http://www.codeproject.com/csharp/genericoperators.asp. Na prática isso não será perceptível, exceto caso haja milhões de linhas no grid ou milhões de usuários agrupando e sumarizando grids de forma concorrente.

No exemplo online, o EnableViewState do grid está definido como False. Isso pois, caso contrário, no PostBack o grid iria recuperar a informação necessária para renderizar seu conteúdo do ViewState, não disparando o evento RowDataBound que é essencial ao funcionamento do GridViewHelper. Considero desabilitar o EnableViewState uma prática recomendável, exceto caso você tenha certeza de que ele é necessário, ou caso o custo da consulta ao banco de dados seja alto. No ASP.Net 1.1 desabilitar o EnableViewState podia afetar o comportamento dos controles em função de não haver a separação entre ViewState (estado visual) e ControlState (estado do controle). Essa divisão foi incluída no ASP.Net 2.0.

Todo o código e inclusive comentários foram em inglês por dois motivos:

1 - Permitir a divulgação em comunidades internacionais, tais com o Code Project
2 - Ninguém merece uma classe de nome AjudanteDoGridView !!

O exemplo e os fontes podem ser baixados aqui. Sintam-se à vontade para comentar, sugerir e, principalmente, ampliar a funcionalidade do GridViewHelper. Enviem as adaptações para que eu possa atualizar o artigo e compartilhar com a comunidade.

Agrinei Sousa

Agrinei Sousa