Formatação Avançada de DataGrid com ItemDataBound

 

Estava querendo produzir DataGrids com as mesmas funcionalidades de relatórios impressos e a coisa toda se parecia com alguns sistemas de gerenciamento de informações muito caros que foram substituídos com o .NET. O produto final deveria focar a atenção dos usuários nos dados desejados, sem as complicações de campos extras e outros detalhes. Este artigo se propõe a introduzir o uso de alguns truques simples para incrementar a aparência dos DataGrids.

Veremos então como fazer para:

·         Criar agrupamentos de colunas

·         Realçar subtotais e totais gerais

·         Realçar células individuais com base em valores de dados

 

Dados SQL e Banco de dados

Foram usados o Northwind e o SQL Server DB para produzir os dados para este artigo, com um subtotal para cada categoria de produto e um total geral no final. O código funcionará para qualquer fonte de dados. As stored procedures SQL foram também incluídas para aqueles que estejam interessados em recriar exatamente o que é mostrado aqui.

 

Quase toda a lógica de negócio foi colocada no lado do banco de dados, usando o DataGrid apenas para fins de exibição, portanto, toda a ordenação e os totais são feitos aqui.

 

CREATE PROCEDURE usp_sales_by_cate AS

 

create table #temp ( Sorty int, CategoryName varchar(50),

         ProductName varchar(50), ProductSales real)

 

-- Get Base Sales

INSERT INTO #temp

SELECT 0, dbo.Categories.CategoryName, dbo.Products.ProductName,

SUM(dbo.[Order Details Extended].ExtendedPrice)

AS ProductSales

FROM dbo.Categories INNER JOIN

dbo.Products INNER JOIN

dbo.Orders INNER JOIN

dbo.[Order Details Extended] ON dbo.Orders.OrderID =

       dbo.[Order Details Extended].OrderID ON

dbo.Products.ProductID =

 dbo.[Order Details Extended].ProductID ON dbo.Categories.CategoryID =

                  dbo.Products.CategoryID

WHERE (dbo.Orders.OrderDate BETWEEN '19970101' e '19971231')

GROUP BY dbo.Categories.CategoryName, dbo.Products.ProductName

ORDER BY dbo.Categories.CategoryName

 

-- Build SubTotal

INSERT INTO #temp

SELECT 1 , CategoryName, 'SubTotal', sum( ProductSales)

from #temp

 

group by CategoryName

-- Build Grand Total

INSERT INTO #temp

SELECT 2 , 'XXXXX', 'Grand Total', sum( ProductSales)

from #temp

Where sorty = 0

 

-- Display Values

SELECT CategoryName, ProductName, ProductSales from #temp

order by CategoryName, Sorty

 

Criando Agrupamentos

Não queremos que as categorias sejam exibidas sempre na primeira coluna, porém apenas quando mudarem, o que dará um aspecto limpo e agradável ao DataGrid, permitindo aos usuários achar os itens rápida e facilmente.

Primeiro, criamos uma variável pública que funcionará para a página inteira e, o mais importantemente, lembrará algo a cada vez que o rodar para cada linha do DataGrid. O Page Load, será também pré-configurado (ocorreram algumas situações estranhas em que isto não aconteceu).

 

public class WebForm1 : System.Web.UI.Page

{

 public string LastColumn;

..

..

 

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

{

// Configurar LastColumn para branco

if (!IsPostBack) LastColumn = "";

 

A seguir, usamos a variável para localizar as alterações nos dados que irão para o DataGrid. Se o valor da célula de cell[0] não for alterado, o texto da mesma será apagado e a borda será removida:

 

e.Item.Cells[0].Style.Add("BORDER", "none").

 

Para ignorar o cabeçalho (header) do DataGrid:

 

if( ( e.Item.ItemType.ToString()!= "Header"))

    {.....

 

Isto também pode ser utilizado para os itens, itens alternativos e rodapés (footers) em um DataGrid, para especializar o código.

 

A seguir, o código completo para agrupar a primeira coluna do DataGrid:

 

private void DataGrid1_ItemDataBound(object sender,

    System.Web.UI.WebControls.DataGridItemEventArgs e)

{

     //Obter o texto da coluna 0 atual

      string CurrentColumn = e.Item.Cells[0].Text;

 

   // Pular Headers

   if( ( e.Item.ItemType.ToString()!= "Header"))

    {

      // Houve alguma alteração

      if (CurrentColumn == LastColumn)

       {

        // Sem alteração na Coluna 0

        //limpar e remover a borda

        e.Item.Cells[0].Text = "";

        e.Item.Cells[0].Style.Add("BORDER", "none");

       }

      else

        {

         // esta eh a primeira da serie

         // atribuir LastColumn para a coluna atual

         LastColumn = CurrentColumn;

        // Adicionar uma cor de fundo para Cell[0]

        e.Item.Cells[0].BackColor =

               System.Drawing.Color.WhiteSmoke;

        }

}

 

Realçando Subtotais e Totais Gerais

 

Isto é bastante simples, caso o texto da célula for "Sub Total" / "Grand Total", então configuramos a fonte, o tipo, as cores, etc.

 

// Verificar se eh um SubTotal

string MyCol2 = e.Item.Cells[1].Text;

 

if (MyCol2 == "SubTotal")

 {

  e.Item.Font.Bold = true;

  e.Item.BackColor = Color.DimGray;

  e.Item.ForeColor = Color.White;

 

  // limpar Column 0

  e.Item.Cells[0].Text = "";

  e.Item.Cells[0].Style.Add("BORDER", "none");

  e.Item.Cells[0].BackColor = Color.Transparent;

 }

 

if (MyCol2 == "Grand Total")

  {

   e.Item.Font.Bold = true;

   e.Item.BackColor = Color.Red;

   e.Item.ForeColor = Color.White;

   e.Item.Cells[0].Style.Add("BORDER", "none");

   e.Item.Cells[0].BackColor = Color.Transparent;

}

 

Realçando Células Individuais

 

Este recurso é amplamente utilizado para exibir valores negativos em vermelho (do modo como se faz em contabilidade). Descobrimos que a conversão de string para número, pode produzir alguns erros estranhos, por isso, geralmente envelopamos as conversões em um try/catch para ignorar qualquer erro que possa surgir.

 

//  Destacar itens caso as vendas estejam abaixo de 5000

string MyStr = e.Item.Cells[2].Text;

 

try

{

  double MyValue = double.Parse(MyStr);

  

  if (MyValue < 5000 )

  {

    e.Item.Cells[2].ForeColor = Color.Red;

    e.Item.Cells[2].Font.Bold = true;

  }

}

catch(Exception)

{

 // numero invalido

}