DataGrid com somatório no rodapé – Parte II
Segundo Passo: Vinculável a qualquer DataSource
Não é fantástico? não escrevemos uma única linha de código no code behind do WebForm e estamos obtendo sum/count/average no Footer. Podemos fazer o mesmo com a coluna Bônus apenas mudando o tipo da coluna SumColumn. Porém temos apenas um pequeno problema. Este controle trabalha bem caso o DataSource do DataGrid for DataTable ou DataReader. Caso tentarmos utilizar objetos personalizados Array ou ArrayList, provavelmente obteremos uma mensagem do tipo:
Figura 4.
Temos que achar uma maneira de manipular qualquer tipo de DataSource. Hmmm... Que tal utilizar a classe PropertyDescriptor do namespace System.ComponentModel para obter ao valor em lugar do objeto subjacente. Atualizamos a seguinte linha de CellItemDataBound:
dValue = DGI.DataItem(DataField)
para a linha abaixo:
dValue = Me.GetUnderlyingValue(dataItem)
e adicionamos um novo método GetUnderlyingValue a nossa classe.
Protected Function GetUnderlyingValue(ByVal dataItem As Object) As Decimal
Dim boundFieldDesc As PropertyDescriptor = _
TypeDescriptor.GetProperties(dataItem).Find(Me.DataField, True)
If(boundFieldDesc Is Nothing) Then
Throw New HttpException("Field Not Found: " + Me.DataField)
End If
Dim dValue As Object = boundFieldDesc.GetValue(dataItem)
Return Decimal.Parse(dValue.ToString())
End Function
Ao invés de apenas puxar o valor do DataItem, estamos dependendo do Método GetUnderlyingValue para obter o valor do DataItem, o qual, por sua vez, utiliza a classe TypeDescriptor para verificar se exista o objeto subjacente DataField. Se for bem sucedido, retornará o valor ao método chamador, caso contrário, levantará Exception. Agora podemos verificar a saída com (quase) todos os tipos de DataSource.
Terceiro Passo: Saída Personalizável
Tudo parece estar de acordo agora, porém não há nenhum controle na saída. E caso desejemos mostrar apenas a soma (nenhuma média e nenhuma contagem)? Alguma outra pessoa desejará mostrar a soma e a média. Existem diferentes possibilidades, portanto, deve haver alguma forma de personalizar às saídas como desejado. Vejamos o que podemos fazer:
#Region " Attributes "
Private internalSum As Decimal
Private internalCount As Integer
Private _ShowSum As Boolean = True
Private _ShowCount As Boolean = True
Private _ShowAverage As Boolean = True
#End Region
#Region " Properties "
Public Property ShowSum() As Boolean
Get
Return _ShowSum
End Get
Set(ByVal Value As Boolean)
_ShowSum = Value
End Set
End Property
Public Property ShowCount() As Boolean
Get
Return _ShowCount
End Get
Set(ByVal Value As Boolean)
_ShowCount = Value
End Set
End Property
Public Property ShowAverage() As Boolean
Get
Return _ShowAverage
End Get
Set(ByVal Value As Boolean)
_ShowAverage = Value
End Set
End Property
#End Region
Expusemos três propriedade públicas, isto é, ShowSum, ShowCount e ShowAverage em nossa classe SumColumn. Podemos utilizar estas propriedade no .aspx para personalizar às saídas. Por exemplo;
DataField="Salary" HeaderText="Salary">
ou
DataField="Salary" HeaderText="Salary">
Internamente em nossa classe, podemos verificar os valores das propriedades no método CellItemDataBound exposto para personalizar às saídas da forma desejada.
Case ListItemType.Footer
If Me._ShowSum = True Then
cell.Text = "Sum : " & Me.FormatDataValue(internalSum) & "
"
End If
If Me._ShowCount = True Then
cell.Text += "Count : " & internalCount & "
"
End If
If Me._ShowAverage = True Then
cell.Text += "Average : " & Me.FormatDataValue(internalSum / internalCount)
End If
End If
Se atualizarmos o código e o .aspx, poderemos obter as seguintes saídas:
Figura 5.
Quarto Passo: Ajustando as Saídas em Tempo de Design
Sim, sabemos que agora está se sentindo sonolento, porém me dê apenas mais cinco minutos. Se não estiver interessado em alterar as saída em tempo de projeto, (isto é quando vemos o DataGrid em tempo de projeto utilizando o Visual Studio), então pode pular esta seção. Para aqueles que ainda estão lendo, verificar as alterações no código abaixo:
Case ListItemType.AlternatingItem, ListItemType.Item
If Me.DesignMode = False Then
dValue = Me.GetUnderlyingValue(dataItem)
.....
.....
cell.Text = Me.FormatDataValue(dValue)
Else
cell.Text = "SumColumn"
End If
Case ListItemType.Footer
If Me.DesignMode = False Then
If Me._ShowSum = True Then
.....
.....
End If
Else
cell.Text = "Total"
End If
Acho que o código é completamente auto-explicativo. Utilizamos simplesmente a propriedade DesignMode da classe base BoundColumn para ajustar as saídas de SumColumn em tempo de projeto.
Figura 6.
Conclusão
Agora já temos a nossa própria nova coluna SumColumn personalizada, derivada de BoundColumn, com a funcionalidade de mostrar os valores sum/average/count das colunas no footer do DataGrid. Este é apenas um exemplo de uma coluna DataGrid reutilizável e depende de você examinar suas próprias aplicações e aquilo que poderia realmente ser envelopado em uma coluna DataGrid personalizada.