Cómo sumar una columna del control DataGridView
Por Enrique Martínez Montejo
Última revisión: 23/07/2011
 

Se pueden utilizar varias técnicas para sumar una columna concreta del control DataGridView, dependiendo si el control se encuentra o no enlazado a un objeto DataTable.

El control DataGridView se encuentra enlazado con un objeto DataTable

En éste supuesto, la manera más fácil y rápida de obtener la suma total de una columna del objeto DataTable, es utilizar su método Compute, método al que se le puede especificar un filtro para que solamente se sumen los valores de la columna de aquellos registros o filas que cumplan con el criterio especificado. Si al filtro le especificamos la palabra clave Nothing, se sumarán los valores de todas las filas del objeto DataTable.

' Referenciamos el objeto DataTable enlazado con el
' control DataGridView.
'

Dim dt As DataTable = DirectCast(DataGridView1.DataSource, DataTable)

' Sumamos todas las filas de la columna TotalFactura.
'

Dim resultado As Object = dt.Compute("Sum(TotalFactura)", Nothing)

TextBox1.Text = String.Format("{0:N2}", resultado)

' Sumamos todas las filas de la columna TotalFactura
' donde el campo Fecha esté comprendido entre el día
' 01/02/2011 y 28/02/2011.
'

resultado = dt.Compute("Sum(TotalFactura)", _
            "Fecha >= #02/01/2011# And Fecha <= #02/28/2011#")

TextBox2.Text = String.Format("{0:N2}", resultado)

Para más información sobre las funciones de agregado y criterios de filtro que puede utilizar con el método Compute, consulte en la ayuda de Visual Studio la propiedad Expression de un objeto DataColumn.

El control DataGridView no se encuentra enlazado

Al no estar el control enlazado a un objeto DataTable, no nos va a quedar más remedio que ir sumando los valores individuales de las celdas de la columna conforme recorremos la colección de filas del control DataGridView, o bien utilizar una consulta LINQ to Objects.

Para los usuarios que no conozcan lo suficientemente LINQ como para utilizarlo con soltura, lo mismo se sentirán más cómodos ejecutando el siguiente bucle:

Dim resultado As Decimal = 0D

For Each row As DataGridViewRow In DataGridView1.Rows

    ' Obtenemos el valor de la celda.
    '

    Dim value As Object = row.Cells("Column1").Value

    Dim d As Decimal

    Dim bln As Boolean = Decimal.TryParse(Convert.ToString(value), d)

    ' Si el valor alfanumérico no se puede convertir a número,
    ' continuamos el bucle.
    '

    If
(Not bln) Then Continue For

    ' Sumamos el valor.
    '

    resultado += Convert.ToDecimal(d)

Next

TextBox1.Text = String.Format("{0:N2}", resultado)

Y los usuarios que prefieran utilizar LINQ (se necesita .NET 3.5 o superior), obtendrían la suma de una columna del control DataGridView de la siguiente manera:

 Try
    ' Ejecutamos la consulta LINQ.
    '

    Dim query As IEnumerable(Of Object) = _
        From row As DataGridViewRow In DataGridView1.Rows.Cast(Of DataGridViewRow)() _
        Where ( _
                (row.Cells("Column1").Value IsNot Nothing) AndAlso _
                (row.Cells("Column1").Value IsNot DBNull.Value) _
        ) Select row.Cells("Column1").Value

    ' Obtenemos la suma.
    '

    Dim resultado As Decimal = _
        query.Sum(Function(row) Convert.ToDecimal(row))

    TextBox1.Text = String.Format("Resultado: {0:N2}", resultado)

Catch ex As Exception
    MessageBox.Show(ex.Message)

End Try

Si el control DataGridView no se encuentra enlazado a datos, hay que tener cuidado con los valores Nothing que puedan tener las celdas de las columnas. Pero si el control se encuentra enlazado a un objeto DataTable, entonces también hay que tener cuidado con los valores NULL. Tenga presente que un valor Nothing es distinto a los valores NULL de las bases de datos.

Añadir métodos de extensión

A partir de la versión 3.5 del marco de trabajo de .NET, podemos añadir nuevos métodos a las clases existentes, incluidas las del propio marco de trabajo de .NET.

Mediante lo que se conoce como métodos de extensión, podemos ampliar las clases ya existentes añadiéndole nuevos métodos, a los cuales llamaríamos de igual manera que llamamos a cualquier otro método de una clase, es decir, como si fuera otro método de instancia de una clase o tipo de dato existente.

Únicamente podemos definir como métodos de extensión, a los procedimientos Sub y Function, por tanto, estarían fuera del concepto los procedimientos de propiedad y evento, así como las definiciones de campo que se incluyan dentro de una clase (variables con ámbito de visibilidad a nivel de la propia clase donde se definen).

Todos los métodos de extensión, necesariamente tienen que estar marcados con el atributo Extension, el cual se encuentra incluido dentro del espacio de nombres System.Runtime.CompilerServices, por tanto, al comienzo del módulo importaríamos dicho espacio de nombres:

Imports System.Runtime.CompilerServices

Otro requisito que debe cumplir cualquier método de extensión, es que su primer parámetro siempre tiene que estar definido con el mismo tipo de dato de la clase que se desea extender. Si vamos a añadir a la clase DataGridView un método de extensión llamado SumColumn, su primer parámetro deberá estar definido como DataGridView. Pero si el método de extensión lo vamos a añadir a la clase DataTable, entonces su primer parámetro deberá estar definido del tipo DataTable. En definitiva, el primer parámetro del método de extensión define la clase a la cual se va añadir dicho método.

En Visual Basic .NET, los métodos de extensión necesariamente deben declararse dentro de un módulo con un ámbito de visibilidad Public; no pueden existir dentro de la definición de una clase. En C# sí se encuentran dentro de una clase, al igual que cualquier otro procedimiento, pero tanto el método de extensión como la propia clase, tienen que estar definidos como estáticos mediante el modificador static.

A continuación vamos a añadir cuatros métodos de extensión para sumar una columna cualquiera: dos métodos de extensión para el objeto DataTable, y otros dos para el control DataGridView:

Imports System.Runtime.CompilerServices

Public Module MetodosExtension

    ''' <summary>
    ''' Devuelve la suma de los valores de una columna de un objeto DataTable.
    ''' </summary>
    ''' <param name="dt">Objeto DataTable.</param>
    ''' <param name="columnName">El nombre de la columna cuyos valores se desean sumar.</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()> _
    Public Function SumColumn(ByVal dt As DataTable, _
                              ByVal columnName As String) As Decimal

        Return dt.SumColumn(columnName, Nothing)

    End Function

    ''' <summary>
    ''' Devuelve la suma de los valores de una columna de un objeto DataTable.
    ''' </summary>
    ''' <param name="dt">Objeto DataTable.</param>
    ''' <param name="columnName">El nombre de la columna cuyos valores se desean sumar.</param>
    ''' <param name="filter">Filtro que limitará las filas que se sumarán.</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()> _
    Public Function SumColumn(ByVal dt As DataTable, _
                              ByVal columnName As String, _
                              ByVal filter As String) As Decimal

        If ((dt Is Nothing) OrElse (String.IsNullOrEmpty(columnName))) Then _
            Return 0D

        Try
            Dim expression As String = String.Format("Sum({0})", columnName)

            Dim value As Object = Nothing

            If (String.IsNullOrEmpty(filter)) Then
                ' Sumamos todas las filas.
                '

                value = dt.Compute(expression, Nothing)

            Else
                ' Sumamos las filas que coincidan con el criterio especificado.
                '

                value = dt.Compute(expression, filter)

            End If

            If (value Is DBNull.Value) Then
                Return 0D

            Else
                Return Convert.ToDecimal(value)

            End If

        Catch ex As Exception
            Return 0D

        End Try

    End Function

    ''' <summary>
    ''' Devuelve la suma de los valores de una columna del control DataGridView.
    ''' </summary>
    ''' <param name="dgv">Control DataGridView.</param>
    ''' <param name="columnName">El nombre de la columna cuyos valores se desean sumar.</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()> _
    Public Function SumColumn(ByVal dgv As DataGridView, _
                              ByVal columnName As String) As Decimal

            Return dgv.SumColumn(columnName, Nothing)

    End Function

    ''' <summary>
    ''' Devuelve la suma de los valores de una columna del control DataGridView.
    ''' </summary>
    ''' <param name="dgv">Control DataGridView.</param>
    ''' <param name="columnName">El nombre de la columna cuyos valores se desean sumar.</param>
    ''' <param name="selectedRows">Colección de filas que se sumarán.</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()> _
    Public Function SumColumn( _
        ByVal dgv As DataGridView, _
        ByVal columnName As String, _
        ByVal selectedRows As DataGridViewSelectedRowCollection) As Decimal

        If ((dgv Is Nothing) OrElse (String.IsNullOrEmpty(columnName))) Then _
            Return 0D

        Try
            ' Si no hay filas seleccionadas, indicamos la intención de
            ' sumar todas las filas del control DataGridView.
            '

            If ((selectedRows IsNot Nothing) AndAlso _
                (selectedRows.Count = 0)) Then _
                selectedRows = Nothing

            Dim query As IEnumerable(Of Object) = Nothing

            If (selectedRows Is Nothing) Then
                ' Se desea sumar todas las filas.
                '

                query = From row As DataGridViewRow In dgv.Rows.Cast(Of DataGridViewRow)() _
                        Where ( _
                                (row.Cells(columnName).Value IsNot Nothing) AndAlso _
                                (row.Cells(columnName).Value IsNot DBNull.Value) _
                        ) Select row.Cells(columnName).Value

            Else
                ' Se desea sumar las filas seleccionadas.
                '

                query = From row As DataGridViewRow In dgv.Rows.Cast(Of DataGridViewRow)() _
                        Where ( _
                                (row.Selected) AndAlso _
                                (row.Cells(columnName).Value IsNot Nothing) AndAlso _
                                (row.Cells(columnName).Value IsNot DBNull.Value) _
                        ) Select row.Cells(columnName).Value
            End If

            ' Devolvemos la suma.
            '

            Return query.Sum(Function(row) Convert.ToDecimal(row))

        Catch ex As Exception
            Return 0D

        End Try

    End Function

End Module

Para llamar a los métodos de extensión de un objeto DataTable que se encuentra enlazado a un control DataGridView, ejecutaría alguna de las siguientes líneas:

' Referenciamos el objeto DataTable enlazado con el
' control DataGridView.
'
Dim
dt As DataTable = DirectCast(DataGridView1.DataSource, DataTable)

' Sumamos todas las filas de la columna Total.
'

Dim
resultado As Decimal = dt.SumColumn("Total")

TextBox1.Text = String.Format("{0:N2}", resultado)

' Sumamos todas las filas de la columna Total
' donde el campo Fecha esté comprendido entre el día
' 01/02/2011 y 28/02/2011.
'
resultado = dt.SumColumn("Total", "Fecha >= #02/01/2011# And Fecha <= #02/28/2011#")

TextBox2.Text = String.Format("{0:N2}", resultado)

Y para llamar a los métodos de extensión del control DataGridView para sumar una columna cualquiera, ejecutarías éstas otras líneas de código:

' Sumamos los valores de todas las filas de la columna Column1.
'
Dim
resultado As Decimal = DataGridView1.SumColumn("Column1")

TextBox1.Text = String.Format("{0:N2}", resultado)

' Sumamos los valores de la columna Column1 cuyas filas estén seleccionadas.
'

resultado = DataGridView1.SumColumn("Column1", DataGridView1.SelectedRows)

TextBox2.Text = String.Format("{0:N2}", resultado)

 

Otros enlaces de interés:

Índice de la colección de ejemplos de las clases del marco de trabajo de .NET


Enrique Martínez Montejo - 2011

NOTA: La información contenida en este artículo, así como el código fuente incluido en el mismo, se proporciona COMO ESTÁ, sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo explicado, recomendado o sugerido en el presente artículo.

NOTE: The information contained in this article and source code included therein, is provided AS IS without warranty of any kind, and confers no rights. You assume any risk to implement, use or run it explained, recommended or suggested in this article.