SummaryDataGridView o cómo mostrar fila de totales en un DataGridView en VisualBasic.NET.
Ayer estaba intentando mostrar una fila de totales en un objeto DataGridView y tras unas horitas de buscar, investigar, probar, depurar y desesperar... descubrí que en realidad no se puede hacer tal cosa de manera directa. Si el DataGridView está enlazado a datos (caso más habitual), no nos permitirá añadir una fila directamente. Sí que se la podremos añadir al propio origen de datos (supongamos que un DataTable), pero aun así tendremos muchos problemas cuando el usuario ordene los resultados del DataGridView por alguna de las columnas simplemente haciendo click sobre su encabezado. Por supuesto podemos eliminar la posibilidad de que se pueda hacer click en los encabezados de las columnas para ordenar los resultados, pero eso no es más que eliminar un problema quitando una funcionalidad importante, así que no se acepta dicha solución.
Al final he optado por crearme mi propio SummaryDataGridView que hereda directamente de la clase DataGridView, con lo cual tiene intactos todos sus métodos, eventos y propiedades y por tanto se puede utilizar en cualquier punto donde quisiéramos utilizar un DataGridView. Pero además incorpora los métodos siguientes:
Debe utilizarse cuando queramos establecer el origen de datos del SummaryDataGridView. Se podría utilizar simplemente la propiedad DataSource, pero en ese caso no funcionarían las características extras del SummaryDataGridView, por lo cual conviene utilizar este método SetDataSource en lugar de la propiedad DataSource. Probablemente se podría haver sobrecargado la propiedad DataSource del DataGridView, pero he preferido dejarla intacta y utilizar un nuevo método público propio.
Añade una última fila al SummaryDataGridView con los totales (sumas) de todas las columnas cuyos nombres se hayan indicado como parámetros.
Permite filtrar los resultados a mostrar en el SummaryDataGridView mediante una condición tipo SQL simple o compuesta. Evidentemente, tras filtrar los resultados mostrados la fila de totales se actualiza convenientemente.
Así pues, una vez tenemos un objeto SummaryDataGridView en nuestro formulario, podemos poblarlo, añadirle una fila de totales y filtrarlo del siguiente modo:
A continuación dejo el código fuente íntegro de esta clase SummaryDataGridView. Y ya sabes... si te ha sido útil tienes la obligación moral de... a) enviarme una transferencia por valor de 500 euros para compensarme por las horas de desarrollo... o bien b) dejar un comentario excusándote por no hacerme la transferencia y contándome qué tal te funcionó el SummaryDataGridView.
' Author: Albert Mata (www.albertmata.net)
' Last time modified: 2009-02-17
' Description: Acts like a standard DataGridView but with the
' possibility to add a summary row fixed at the
' end even when user sorts data.
'--------------------------------------------------------------------
Public Class SummaryDataGridView
Inherits System.Windows.Forms.DataGridView
#Region "PrivateAttributes"
'----------------------------------------------------------------
' Private attributes.
'----------------------------------------------------------------
'After it's been set, it will not change anymore.
Private MainDataTable As DataTable
'It can be Nothing and it changes every time filter does.
Private SummaryDataRow As DataRow
'It always has same data shown in DataGridView.
Private ShownDataTable As DataTable
'It's Nothing or it stores a column name.
Private LastSortedColumn As String
'It's Nothing or it stores ASC/DESC string value.
Private LastSortOrder As String
'Array of column names to summarize.
Private SummaryColumns() As String
#End Region
#Region "PublicMethods"
'----------------------------------------------------------------
' Sets DataSource and stores initial DataTable.
'----------------------------------------------------------------
Public Sub SetDataSource(ByVal dt As DataTable)
'Setting MainDataTable and adding auxiliar column
'for SummaryRow information.
Me.MainDataTable = dt
Me.MainDataTable.Columns.Add(New DataColumn("@SummaryRow", _
System.Type.GetType("System.Byte")))
'Setting new auxiliar column value to 0
'(in SummaryRow it'll be 1).
For Each dr As DataRow In Me.MainDataTable.Rows
dr.Item("@SummaryRow") = 0
Next
'Filling DataGridView with ShownDataTable and hidding
'auxiliar column.
Me.ShownDataTable = Me.MainDataTable.Copy()
Me.DataSource = Me.ShownDataTable
Me.Columns("@SummaryRow").Visible = False
End Sub
'----------------------------------------------------------------
' Adds a summary row to DataGridView with values in all
' specified columns.
'----------------------------------------------------------------
Public Sub AddSummaryRow(ByVal ParamArray ColumnName() As String)
'Removing SummaryRow from ShownDataTable
'if it already contains it.
If Me.ShownDataTable.Rows.IndexOf(Me.SummaryDataRow) > -1 _
Then
Me.ShownDataTable.Rows.Remove(Me.SummaryDataRow)
End If
'Storing columns to be summarized
'(can be useful for other methods).
Me.SummaryColumns = ColumnName
'Creating SummaryDataRow.
Me.SummaryDataRow = Me.ShownDataTable.NewRow()
'Adding SUM values to each specified column.
For i As Integer = 0 To ColumnName.Length - 1
Me.SummaryDataRow.Item(ColumnName(i)) = _
Me.ShownDataTable.Compute("SUM(" & ColumnName(i) _
& ")", "")
Next
'Setting auxiliar column value to 1
'(useful for sorting purposes).
Me.SummaryDataRow.Item("@SummaryRow") = 1
'Adding TotalsDataRow to ShownDataTable.
Me.ShownDataTable.Rows.Add(Me.SummaryDataRow)
End Sub
'----------------------------------------------------------------
' Sets a filter to DataGridView.
'----------------------------------------------------------------
Public Sub SetFilter(ByVal Filter As String)
'Creating DataView with desired filter.
Dim dv As New DataView(Me.MainDataTable, Filter, "", _
DataViewRowState.CurrentRows)
Me.ShownDataTable = dv.ToTable()
'Adding new calculated SummaryRow if it already exists.
If Not IsNothing(Me.SummaryDataRow) Then
Me.AddSummaryRow(Me.SummaryColumns)
End If
'Creating sorting SQL clausule.
Dim OrderBy As String = ""
If Not IsNothing(Me.LastSortedColumn) _
And Not IsNothing(Me.LastSortOrder) Then
OrderBy = "@SummaryRow ASC, " _
& Me.LastSortedColumn & " " & Me.LastSortOrder
End If
'Sorting DataView.
dv = New DataView(Me.ShownDataTable, "", OrderBy, _
DataViewRowState.CurrentRows)
'Filling DataGridView with DataView.
Me.DataSource = dv
End Sub
#End Region
#Region "EventHandlers"
'----------------------------------------------------------------
' Event Me.Sorted.
'----------------------------------------------------------------
Private Sub ExtendedDataGridView_Sorted(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Sorted
'Setting sorted column and sort order.
If Me.SortedColumn.Name = Me.LastSortedColumn Then
If Me.LastSortOrder = "ASC" Then
Me.LastSortOrder = "DESC"
Else
Me.LastSortOrder = "ASC"
End If
Else
Me.LastSortedColumn = Me.SortedColumn.Name
Me.LastSortOrder = "ASC"
End If
'Creating sorting SQL clausule.
Dim OrderBy As String = "@SummaryRow ASC, " _
& Me.LastSortedColumn & " " _
& Me.LastSortOrder
'Creating DataView with desired sort order.
Dim dv As New DataView(Me.ShownDataTable, "", OrderBy, _
DataViewRowState.CurrentRows)
'Filling DataGridView with DataView.
Me.DataSource = dv
End Sub
#End Region
End Class
Tags: .NET, datagridview, datarow, datatable, sort, summary, visual basic