Criando grupos e totalizações no GridView com apenas 2 linhas de código
Site sobre desenvolvimento de sites multi-linguagem lançado: www.SitesMultilinguagem.com |
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.
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:
ShipRegion | ShipName | OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|---|---|
RJ | Hanari Carnes | 10922 | Alice Mutton | 15 | R$ 39,00 | R$ 585,00 |
RJ | Hanari Carnes | 10922 | Guaraná Fantástica | 35 | R$ 4,50 | R$ 157,50 |
RJ | Hanari Carnes | 10925 | Inlagd Sill | 25 | R$ 19,00 | R$ 475,00 |
RJ | Hanari Carnes | 10925 | Filo Mix | 12 | R$ 7,00 | R$ 84,00 |
SP | Wellington Importadora | 10935 | Chai | 21 | R$ 18,00 | R$ 378,00 |
SP | Wellington Importadora | 10935 | Carnarvon Tigers | 4 | R$ 62,50 | R$ 250,00 |
SP | Wellington Importadora | 10935 | Tunnbröd | 8 | R$ 9,00 | R$ 72,00 |
SP | Gourmet Lanchonetes | 10959 | Rhönbräu Klosterbier | 20 | R$ 7,75 | R$ 155,00 |
SP | Queen Cozinha | 10961 | Filo Mix | 6 | R$ 7,00 | R$ 42,00 |
SP | Queen Cozinha | 10961 | Lakkalikööri | 60 | R$ 18,00 | R$ 1.080,00 |
SP | Comércio Mineiro | 10969 | Spegesild | 9 | R$ 12,00 | R$ 108,00 |
RJ | Hanari Carnes | 10981 | Côte de Blaye | 60 | R$ 263,50 | R$ 15.810,00 |
RJ | Que Delícia | 10989 | Grandma's Boysenberry Spread | 40 | R$ 25,00 | R$ 1.000,00 |
RJ | Que Delícia | 10989 | Queso Cabrales | 15 | R$ 21,00 | R$ 315,00 |
RJ | Que Delícia | 10989 | Jack's New England Clam Chowder | 4 | R$ 9,65 | R$ 38,60 |
Para criar um total para a coluna ItemTotal são necessárias apenas as prometidas 2 linhas de código:
Primeiro criamos a classe GridViewHelper especificando no constructor o grid no qual ela irá atuar. Em seguida registramos o sumário informando o nome da coluna e a operação de sumarização desejada. O resultado pode ser visto abaixo:
ShipRegion | ShipName | OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|---|---|
RJ | Hanari Carnes | 10922 | Alice Mutton | 15 | R$ 39,00 | R$ 585,00 |
RJ | Hanari Carnes | 10922 | Guaraná Fantástica | 35 | R$ 4,50 | R$ 157,50 |
RJ | Hanari Carnes | 10925 | Inlagd Sill | 25 | R$ 19,00 | R$ 475,00 |
RJ | Hanari Carnes | 10925 | Filo Mix | 12 | R$ 7,00 | R$ 84,00 |
SP | Wellington Importadora | 10935 | Chai | 21 | R$ 18,00 | R$ 378,00 |
SP | Wellington Importadora | 10935 | Carnarvon Tigers | 4 | R$ 62,50 | R$ 250,00 |
SP | Wellington Importadora | 10935 | Tunnbröd | 8 | R$ 9,00 | R$ 72,00 |
SP | Gourmet Lanchonetes | 10959 | Rhönbräu Klosterbier | 20 | R$ 7,75 | R$ 155,00 |
SP | Queen Cozinha | 10961 | Filo Mix | 6 | R$ 7,00 | R$ 42,00 |
SP | Queen Cozinha | 10961 | Lakkalikööri | 60 | R$ 18,00 | R$ 1.080,00 |
SP | Comércio Mineiro | 10969 | Spegesild | 9 | R$ 12,00 | R$ 108,00 |
RJ | Hanari Carnes | 10981 | Côte de Blaye | 60 | R$ 263,50 | R$ 15.810,00 |
RJ | Que Delícia | 10989 | Grandma's Boysenberry Spread | 40 | R$ 25,00 | R$ 1.000,00 |
RJ | Que Delícia | 10989 | Queso Cabrales | 15 | R$ 21,00 | R$ 315,00 |
RJ | Que Delícia | 10989 | Jack's New England Clam Chowder | 4 | R$ 9,65 | R$ 38,60 |
R$ 20.550,10 |
No exemplo acima foi adicionada uma nova linha ao grid para o sumário. Uma outra opção é utilizar a própria linha de rodapé (footer) para exibição do sumário ao invés de adicionar uma nova linha. Quando uma nova linha é adicionada ao grid, apenas as células necessárias para exibição dos colunas sumarizadas são criadas. Com a utilização do rodapé todas as células são criadas. No caso de sumários de grupo, a geração de todas as colunas ou apenas das colunas de sumário é um atributo do grupo.
Vamos agora criar um grupo no GridView. O código necessário para o primeiro agrupamento é exibido abaixo:
O primeiro parâmetro do método RegisterGroup informa a coluna para a qual deve ser criado o grupo. É possível também criar um grupo composto por um conjunto de colunas, bastando para isso passar um array com os nomes das colunas que compõem o grupo. O segundo parâmetro informa se o grupo é automático. Nesse caso é criada automaticamente a linha de cabeçalho do grupo. O terceiro parâmetro informa se as colunas que compõem o grupo devem ser ocultadas. O método ApplyGroupSort() define que o grid deve ser ordenado pela(s) coluna(s) do grupo, no caso acima ShipRegion. Isso é requerido para que o agrupamento funcione adequadamente, exceto caso os dados já venham ordenados do banco pela(s) coluna(s) do grupo.
No exemplo abaixo a coluna do grupo (ShipRegion) foi automaticamente ocultada do grid:
ShipName | OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|---|
RJ | |||||
Hanari Carnes | 10922 | Alice Mutton | 15 | R$ 39,00 | R$ 585,00 |
Hanari Carnes | 10922 | Guaraná Fantástica | 35 | R$ 4,50 | R$ 157,50 |
Hanari Carnes | 10925 | Inlagd Sill | 25 | R$ 19,00 | R$ 475,00 |
Hanari Carnes | 10925 | Filo Mix | 12 | R$ 7,00 | R$ 84,00 |
Hanari Carnes | 10981 | Côte de Blaye | 60 | R$ 263,50 | R$ 15.810,00 |
Que Delícia | 10989 | Grandma's Boysenberry Spread | 40 | R$ 25,00 | R$ 1.000,00 |
Que Delícia | 10989 | Queso Cabrales | 15 | R$ 21,00 | R$ 315,00 |
Que Delícia | 10989 | Jack's New England Clam Chowder | 4 | R$ 9,65 | R$ 38,60 |
SP | |||||
Wellington Importadora | 10935 | Chai | 21 | R$ 18,00 | R$ 378,00 |
Wellington Importadora | 10935 | Carnarvon Tigers | 4 | R$ 62,50 | R$ 250,00 |
Wellington Importadora | 10935 | Tunnbröd | 8 | R$ 9,00 | R$ 72,00 |
Gourmet Lanchonetes | 10959 | Rhönbräu Klosterbier | 20 | R$ 7,75 | R$ 155,00 |
Queen Cozinha | 10961 | Filo Mix | 6 | R$ 7,00 | R$ 42,00 |
Queen Cozinha | 10961 | Lakkalikööri | 60 | R$ 18,00 | R$ 1.080,00 |
Comércio Mineiro | 10969 | Spegesild | 9 | R$ 12,00 | R$ 108,00 |
Vamos tornar as coisas um pouco mais interessantes, adicionando um sumário para o grupo criado. Precisamos incluir uma linha para registrar um sumário para o grupo criado anteriormente:
Dessa vez há um parâmetro adicional para o método RegisterSummary. Ele especifica o nome do grupo para o qual o sumário deve ser gerado. O nome do grupo é gerado automáticamente a partir dos nomes das colunas que compõem o grupo. Caso o grupo seja composto por apenas uma coluna, o nome do grupo é o nome da própria coluna. Caso seja composto por mais de uma coluna, o nome do grupo é a concatenação ordenada das colunas que compõem o grupo, separadas por um sinal de "+". Ex: "ShipRegion+ShipName".
Abaixo vemos nosso grid com agrupamento e sumário para o grupo:
ShipName | OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|---|
RJ | |||||
Hanari Carnes | 10922 | Alice Mutton | 15 | R$ 39,00 | R$ 585,00 |
Hanari Carnes | 10922 | Guaraná Fantástica | 35 | R$ 4,50 | R$ 157,50 |
Hanari Carnes | 10925 | Inlagd Sill | 25 | R$ 19,00 | R$ 475,00 |
Hanari Carnes | 10925 | Filo Mix | 12 | R$ 7,00 | R$ 84,00 |
Hanari Carnes | 10981 | Côte de Blaye | 60 | R$ 263,50 | R$ 15.810,00 |
Que Delícia | 10989 | Grandma's Boysenberry Spread | 40 | R$ 25,00 | R$ 1.000,00 |
Que Delícia | 10989 | Queso Cabrales | 15 | R$ 21,00 | R$ 315,00 |
Que Delícia | 10989 | Jack's New England Clam Chowder | 4 | R$ 9,65 | R$ 38,60 |
R$ 18.465,10 | |||||
SP | |||||
Wellington Importadora | 10935 | Chai | 21 | R$ 18,00 | R$ 378,00 |
Wellington Importadora | 10935 | Carnarvon Tigers | 4 | R$ 62,50 | R$ 250,00 |
Wellington Importadora | 10935 | Tunnbröd | 8 | R$ 9,00 | R$ 72,00 |
Gourmet Lanchonetes | 10959 | Rhönbräu Klosterbier | 20 | R$ 7,75 | R$ 155,00 |
Queen Cozinha | 10961 | Filo Mix | 6 | R$ 7,00 | R$ 42,00 |
Queen Cozinha | 10961 | Lakkalikööri | 60 | R$ 18,00 | R$ 1.080,00 |
Comércio Mineiro | 10969 | Spegesild | 9 | R$ 12,00 | R$ 108,00 |
R$ 2.085,00 |
Podemos incluir um sumário geral (grand total) para o grid acima simplesmente utilizando a sobrecarga adequada do método RegisterSummary, como foi mostrado no primeiro exemplo.
É possível criar mais de um grupo no grid, simulando um agrupamento hierárquico, como vemos abaixo:
Resultado:
OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|
RJ | ||||
Hanari Carnes | ||||
10922 | Alice Mutton | 15 | R$ 39,00 | R$ 585,00 |
10922 | Guaraná Fantástica | 35 | R$ 4,50 | R$ 157,50 |
10925 | Inlagd Sill | 25 | R$ 19,00 | R$ 475,00 |
10925 | Filo Mix | 12 | R$ 7,00 | R$ 84,00 |
10981 | Côte de Blaye | 60 | R$ 263,50 | R$ 15.810,00 |
Que Delícia | ||||
10989 | Grandma's Boysenberry Spread | 40 | R$ 25,00 | R$ 1.000,00 |
10989 | Queso Cabrales | 15 | R$ 21,00 | R$ 315,00 |
10989 | Jack's New England Clam Chowder | 4 | R$ 9,65 | R$ 38,60 |
SP | ||||
Comércio Mineiro | ||||
10969 | Spegesild | 9 | R$ 12,00 | R$ 108,00 |
Gourmet Lanchonetes | ||||
10959 | Rhönbräu Klosterbier | 20 | R$ 7,75 | R$ 155,00 |
Queen Cozinha | ||||
10961 | Filo Mix | 6 | R$ 7,00 | R$ 42,00 |
10961 | Lakkalikööri | 60 | R$ 18,00 | R$ 1.080,00 |
Wellington Importadora | ||||
10935 | Chai | 21 | R$ 18,00 | R$ 378,00 |
10935 | Carnarvon Tigers | 4 | R$ 62,50 | R$ 250,00 |
10935 | Tunnbröd | 8 | R$ 9,00 | R$ 72,00 |
A visualização fica um pouco prejudicada quando há mais de um grupo. O GridViewHelper disponibiliza eventos para simplificar ajustes visuais ou funcionais no grid. Abaixo vemos a lista de eventos disponíveis:
GroupStart | Ocorre quando um novo grupo se inicia, ou seja, quando novos valores são encontrados na(s) coluna(s) do grupo |
GroupEnd | Ocorre na última linha encontrada para o grupo |
GroupHeader | Ocorre quando uma linha automática de header é adicionada ao grupo. O evento não é disparado caso o grupo não seja automática pois o header não será adicionado automaticamente. |
GroupSummary | Ocorre quando é gerada a linha de sumário do grupo. Também só é disparado caso exista algum sumário automático que force a geração da linha, ou caso haja um grupo de supressão (será visto em seguida). |
GeneralSummary | Ocorre após os sumários gerais serem calculados. Se o sumário for automático o evento ocorrerá após a linha de sumário ser incluída e os valores serem definidos. |
FooterDataBound | Ocorre no databinding do footer. |
Com mais algumas (poucas) linhas de código conseguimos melhorar o aspecto visual de nosso grid:
Nosso grid após os cosméticos:
OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|
RJ | ||||
Hanari Carnes | ||||
10922 | Alice Mutton | 15 | R$ 39,00 | R$ 585,00 |
10922 | Guaraná Fantástica | 35 | R$ 4,50 | R$ 157,50 |
10925 | Inlagd Sill | 25 | R$ 19,00 | R$ 475,00 |
10925 | Filo Mix | 12 | R$ 7,00 | R$ 84,00 |
10981 | Côte de Blaye | 60 | R$ 263,50 | R$ 15.810,00 |
Que Delícia | ||||
10989 | Grandma's Boysenberry Spread | 40 | R$ 25,00 | R$ 1.000,00 |
10989 | Queso Cabrales | 15 | R$ 21,00 | R$ 315,00 |
10989 | Jack's New England Clam Chowder | 4 | R$ 9,65 | R$ 38,60 |
SP | ||||
Comércio Mineiro | ||||
10969 | Spegesild | 9 | R$ 12,00 | R$ 108,00 |
Gourmet Lanchonetes | ||||
10959 | Rhönbräu Klosterbier | 20 | R$ 7,75 | R$ 155,00 |
Queen Cozinha | ||||
10961 | Filo Mix | 6 | R$ 7,00 | R$ 42,00 |
10961 | Lakkalikööri | 60 | R$ 18,00 | R$ 1.080,00 |
Wellington Importadora | ||||
10935 | Chai | 21 | R$ 18,00 | R$ 378,00 |
10935 | Carnarvon Tigers | 4 | R$ 62,50 | R$ 250,00 |
10935 | Tunnbröd | 8 | R$ 9,00 | R$ 72,00 |
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:
OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|
RJ - Hanari Carnes | ||||
10922 | Alice Mutton | 15 | R$ 39,00 | R$ 585,00 |
10922 | Guaraná Fantástica | 35 | R$ 4,50 | R$ 157,50 |
10925 | Inlagd Sill | 25 | R$ 19,00 | R$ 475,00 |
10925 | Filo Mix | 12 | R$ 7,00 | R$ 84,00 |
10981 | Côte de Blaye | 60 | R$ 263,50 | R$ 15.810,00 |
RJ - Que Delícia | ||||
10989 | Grandma's Boysenberry Spread | 40 | R$ 25,00 | R$ 1.000,00 |
10989 | Queso Cabrales | 15 | R$ 21,00 | R$ 315,00 |
10989 | Jack's New England Clam Chowder | 4 | R$ 9,65 | R$ 38,60 |
SP - Comércio Mineiro | ||||
10969 | Spegesild | 9 | R$ 12,00 | R$ 108,00 |
SP - Gourmet Lanchonetes | ||||
10959 | Rhönbräu Klosterbier | 20 | R$ 7,75 | R$ 155,00 |
SP - Queen Cozinha | ||||
10961 | Filo Mix | 6 | R$ 7,00 | R$ 42,00 |
10961 | Lakkalikööri | 60 | R$ 18,00 | R$ 1.080,00 |
SP - Wellington Importadora | ||||
10935 | Chai | 21 | R$ 18,00 | R$ 378,00 |
10935 | Carnarvon Tigers | 4 | R$ 62,50 | R$ 250,00 |
10935 | Tunnbröd | 8 | R$ 9,00 | R$ 72,00 |
Podemos adicionar um sumário para o grupo composto. Dessa vez vamos usar a operação média e acrescentar um rótulo para indicar tal operação
OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|
RJ - Hanari Carnes | ||||
10922 | Alice Mutton | 15 | R$ 39,00 | R$ 585,00 |
10922 | Guaraná Fantástica | 35 | R$ 4,50 | R$ 157,50 |
10925 | Inlagd Sill | 25 | R$ 19,00 | R$ 475,00 |
10925 | Filo Mix | 12 | R$ 7,00 | R$ 84,00 |
10981 | Côte de Blaye | 60 | R$ 263,50 | R$ 15.810,00 |
Média | R$ 3.422,30 | |||
RJ - Que Delícia | ||||
10989 | Grandma's Boysenberry Spread | 40 | R$ 25,00 | R$ 1.000,00 |
10989 | Queso Cabrales | 15 | R$ 21,00 | R$ 315,00 |
10989 | Jack's New England Clam Chowder | 4 | R$ 9,65 | R$ 38,60 |
Média | R$ 451,20 | |||
SP - Comércio Mineiro | ||||
10969 | Spegesild | 9 | R$ 12,00 | R$ 108,00 |
Média | R$ 108,00 | |||
SP - Gourmet Lanchonetes | ||||
10959 | Rhönbräu Klosterbier | 20 | R$ 7,75 | R$ 155,00 |
Média | R$ 155,00 | |||
SP - Queen Cozinha | ||||
10961 | Filo Mix | 6 | R$ 7,00 | R$ 42,00 |
10961 | Lakkalikööri | 60 | R$ 18,00 | R$ 1.080,00 |
Média | R$ 561,00 | |||
SP - Wellington Importadora | ||||
10935 | Chai | 21 | R$ 18,00 | R$ 378,00 |
10935 | Carnarvon Tigers | 4 | R$ 62,50 | R$ 250,00 |
10935 | Tunnbröd | 8 | R$ 9,00 | R$ 72,00 |
Média | R$ 233,33 |
Nosso último exemplo de sumarização vai criar um grupo de supressão. É interessante ressaltar que, se for definido um grupo de supressão, nenhum outro grupo pode ser criado. Da mesma maneira, caso haja algum grupo já criado, a tentativa de definir um grupo de supressão irá gerar um exceção.
Abaixo vemos a listagem e a aparência do grid com grupo de supressão:
ShipRegion | ShipName | OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|---|---|
Comércio Mineiro | 9 | R$ 108,00 | ||||
Gourmet Lanchonetes | 20 | R$ 155,00 | ||||
Hanari Carnes | 147 | R$ 17.111,50 | ||||
Que Delícia | 59 | R$ 1.353,60 | ||||
Queen Cozinha | 66 | R$ 1.122,00 | ||||
Wellington Importadora | 33 | R$ 700,00 |
Não é exibido nenhum valor para as colunas que não possuem operação de sumarização. Isso é coerente pois o GridViewHelper não sabe como proceder para sumarizar os valores das n linhas do grupo para um único valor. Da mesma forma, em SQL, se especificarmos no SELECT colunas sem função de agregação que não estejam na cláusula group by receberemos uma mensagem parecida com a exibida abaixo:
"Column 'column_name' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
É interessante ocultar essas colunas já que elas não fazem sentido por não possuírem função de sumarização nem pertencerem ao grupo de supressão. Para isso precisamos chamar mais um método:
Realmente SetInvisibleColumnsWithoutGroupSummary é um nome muito extenso, mas a intenção foi torná-lo o mais descritivo possível. O resultado pode ser visto abaixo:
ShipName | Quantity | ItemTotal |
---|---|---|
Comércio Mineiro | 9 | R$ 108,00 |
Gourmet Lanchonetes | 20 | R$ 155,00 |
Hanari Carnes | 147 | R$ 17.111,50 |
Que Delícia | 59 | R$ 1.353,60 |
Queen Cozinha | 66 | R$ 1.122,00 |
Wellington Importadora | 33 | R$ 700,00 |
Operações de sumarização
O GridViewHelper suporta "nativamente" 3 operações de sumarização: soma, média e contagem de linhas. Uma característica muito interessante é a possibilidade de se especificar operações de sumarização customizadas. Para isso devemos fornecer ao GridViewHelper duas funções: uma que será chamada para cada linha encontrada no grid (ou grupo) e outra para ser consultada e informar o resultado da operação. Abaixo vemos um exemplo de sumarização com operação customizada. A operação semi-dummy criada para esse exemplo retorna o item de menor valor da coluna especificada:
No código acima pode-se ver a assinatura requerida dos métodos. Ambos recebem o nome da coluna e do grupo sendo sumarizados. Caso o sumário não seja relativo a um grupo, o parâmetro group terá valor nulo. O método que é chamado para cada linha encontrada no grid (ou grupo) recebe, além do nome da coluna e do nome do grupo, o valor da coluna na linha corrente.
O resultado é visto abaixo:
ShipRegion | ShipName | OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|---|---|
RJ | Hanari Carnes | 10922 | Alice Mutton | 15 | R$ 39,00 | R$ 585,00 |
RJ | Hanari Carnes | 10922 | Guaraná Fantástica | 35 | R$ 4,50 | R$ 157,50 |
RJ | Hanari Carnes | 10925 | Inlagd Sill | 25 | R$ 19,00 | R$ 475,00 |
RJ | Hanari Carnes | 10925 | Filo Mix | 12 | R$ 7,00 | R$ 84,00 |
SP | Wellington Importadora | 10935 | Chai | 21 | R$ 18,00 | R$ 378,00 |
SP | Wellington Importadora | 10935 | Carnarvon Tigers | 4 | R$ 62,50 | R$ 250,00 |
SP | Wellington Importadora | 10935 | Tunnbröd | 8 | R$ 9,00 | R$ 72,00 |
SP | Gourmet Lanchonetes | 10959 | Rhönbräu Klosterbier | 20 | R$ 7,75 | R$ 155,00 |
SP | Queen Cozinha | 10961 | Filo Mix | 6 | R$ 7,00 | R$ 42,00 |
SP | Queen Cozinha | 10961 | Lakkalikööri | 60 | R$ 18,00 | R$ 1.080,00 |
SP | Comércio Mineiro | 10969 | Spegesild | 9 | R$ 12,00 | R$ 108,00 |
RJ | Hanari Carnes | 10981 | Côte de Blaye | 60 | R$ 263,50 | R$ 15.810,00 |
RJ | Que Delícia | 10989 | Grandma's Boysenberry Spread | 40 | R$ 25,00 | R$ 1.000,00 |
RJ | Que Delícia | 10989 | Queso Cabrales | 15 | R$ 21,00 | R$ 315,00 |
RJ | Que Delícia | 10989 | Jack's New England Clam Chowder | 4 | R$ 9,65 | R$ 38,60 |
4 |
Limitações
Pouco acima, no exemplo de criação de mais de um grupo no mesmo grid, um detalhe pode ter chamado a atenção de alguns:
"É possível criar mais de um grupo no grid, simulando um agrupamento hierárquico"
Apesar da impressão visual ser de um agrupamento hierárquico, a implementação atual não é hierárquica. Não existe grupo e subgrupo, apenas grupos que são registrados sequencialmente. Isso só se torna um problema caso queiramos criar um sumário para um grupo interno, ou melhor, um grupo que não tenha sido o primeiro grupo a ser registrado. Abaixo vemos o que acontece nessa situação:
OrderId | ProductName | Quantity | UnitPrice | ItemTotal |
---|---|---|---|---|
RJ | ||||
Hanari Carnes | ||||
10922 | Alice Mutton | 15 | R$ 39,00 | R$ 585,00 |
10922 | Guaraná Fantástica | 35 | R$ 4,50 | R$ 157,50 |
10925 | Inlagd Sill | 25 | R$ 19,00 | R$ 475,00 |
10925 | Filo Mix | 12 | R$ 7,00 | R$ 84,00 |
10981 | Côte de Blaye | 60 | R$ 263,50 | R$ 15.810,00 |
[ Summary for ShipName Hanari Carnes ] | R$ 17.111,50 | |||
Que Delícia | ||||
10989 | Grandma's Boysenberry Spread | 40 | R$ 25,00 | R$ 1.000,00 |
10989 | Queso Cabrales | 15 | R$ 21,00 | R$ 315,00 |
10989 | Jack's New England Clam Chowder | 4 | R$ 9,65 | R$ 38,60 |
SP | ||||
[ Summary for ShipName Que Delícia ] | R$ 1.353,60 | |||
Comércio Mineiro | ||||
10969 | Spegesild | 9 | R$ 12,00 | R$ 108,00 |
[ Summary for ShipName Comércio Mineiro ] | R$ 108,00 | |||
Gourmet Lanchonetes | ||||
10959 | Rhönbräu Klosterbier | 20 | R$ 7,75 | R$ 155,00 |
[ Summary for ShipName Gourmet Lanchonetes ] | R$ 155,00 | |||
Queen Cozinha | ||||
10961 | Filo Mix | 6 | R$ 7,00 | R$ 42,00 |
10961 | Lakkalikööri | 60 | R$ 18,00 | R$ 1.080,00 |
[ Summary for ShipName Queen Cozinha ] | R$ 1.122,00 | |||
Wellington Importadora | ||||
10935 | Chai | 21 | R$ 18,00 | R$ 378,00 |
10935 | Carnarvon Tigers | 4 | R$ 62,50 | R$ 250,00 |
10935 | Tunnbröd | 8 | R$ 9,00 | R$ 72,00 |
[ Summary for ShipName Wellington Importadora ] | R$ 700,00 | |||
R$ 20.550,10 |
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 quatro classes auxiliares:
GridViewSummary e GridViewGroup, que representam os elementos sumário e grupo;
GridViewSummaryList e GridViewGroupList, que foram criadas para permitir o acesso aos sumários e grupos com um indexador string: helper.GeneralSummaries["ItemTotal"].Value.
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:
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. Há algumas outras opções que não foram exibidas aqui, mas que podem ser facilmente verificadas e compreendidas com o intellisense do Visual Studio.
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.
Site sobre desenvolvimento de sites multi-linguagem lançado: www.SitesMultilinguagem.com |