DataGrid de .NET 1.1 – Cambiando el estilo a las filas

Hola!

Al leer el título, probablemente pensaréis que me he equivocado. Geeks no es precisamente un sitio donde se hable de tecnologías asentadas como .NET 1.1, sino más bien se escribe sobre lo último de lo último. Y es cierto 😉

Pero sin embargo, mucha gente sigue trabajando con .NET 1.1. No todo el mundo puede o quiere migrar a 2.0, pese a que las ventajas son muy interesantes, bien sea por temas de presupuesto, porque el cliente no está dispuesto a asumir el coste de una migración (es un gasto al que un gestor de proyecto no tiene por qué verle las suficientes ventajas), etc.

Hace poco una persona me comentó que no podía cambiar el color de fondo de una fila de un DataGrid en .NET 1.1 en función del valor de uno de los campos de cada fila. Como ese tipo de preguntas son de las que más me estimula (leyendo los dos posts anteriores habréis podido comprobar que soy un poco tocapelotas, pero me gusta bastante ayudar si puedo a la gente), pues me apreté los machos y empecé a indagar cómo se podría hacer.

En .NET 2.0 es bastante sencillo, podemos utilizar la propiedad Rows, que nos devuelve una colección de DataGridViewRow. Una vez tenemos el DataGridViewRow, podemos acceder al elemento enlazado a través de la propiedad DataBoundItem, y podemos modificar el estilo de la fila usando la propiedad DefaultCellStyle. Todo encaja.

Lo primero que pensé fué… Bueno, seguro que no es tan complicado… Voy a la colección Rows del DataGrid, y ahí seguro que puedo cambiarle el BackColor. ¡ERROR! ¡No existe una colección Rows! Ni ninguna que defina las filas del DataGrid desde el punto de vista del interfaz de usuario! (¿Quién diseñó el modelo de objetos del DataGrid de la versión 1.1? ¡Es peor aún que el de Syncfusion!)

Después de andar mirando la documentación un buen rato, y ver que sólo se podían modificar estilos y columnas, decidí tirar por la calle de enmedio. La idea es la siguiente:

  • Heredar de la clase DataGridTextBoxColumn.
  • Sobreescribir el método Paint de la clase.
  • Crear un evento en la clase que pida el color a emplear.

Si lo pensáis, estoy haciendo trampas, porque esto implica que todas las columnas del DataGrid tendrán que ser de esta clase que he creado para que la fila completa aparezca coloreada. Además, dejo como responsabilidad al usuario de la columna el decidir si dicha columna deberá ser coloreada o no. (El código del artículo está disponible en el adjunto).

Al final, el código de la columna era el siguiente: (es VB.NET, que era el lenguaje que me habían pedido utilizar 😛 )

Public Class RowColorEventArgs
    Inherits EventArgs
 
    Public Sub New(ByVal col As color, _
                   ByVal currManager As CurrencyManager, _
                   ByVal rowNum As Integer)
        Me.color = col
        Me.src = currManager
        Me.row = rowNum
    End Sub
 
    Private color As color
    Private src As CurrencyManager
    Private row As Integer
 
    ' Color de la fila.
    Public Property RowColor() As color
        Get
            Return color
        End Get
        Set(ByVal Value As color)
            color = Value
        End Set
    End Property
 
    ' Fuente de datos
    Public ReadOnly Property Source() As CurrencyManager
        Get
            Return src
        End Get
    End Property
 
    ' Indice de la fila.
    Public ReadOnly Property RowIndex() As Integer
        Get
            Return row
        End Get
    End Property
End Class
 
Public Class DataGridColoredTextBoxColumn
    Inherits DataGridTextBoxColumn
 
    Public Event GetForeColor(ByVal sender As Object, _
                              ByVal e As RowColorEventArgs)
    Public Event GetBackColor(ByVal sender As Object, _
                              ByVal e As RowColorEventArgs)
 
    Protected Overloads Overrides Sub Paint(ByVal g As System.Drawing.Graphics, _
              ByVal bounds As System.Drawing.Rectangle, _
              ByVal source As System.Windows.Forms.CurrencyManager, _
              ByVal rowNum As Integer, _
              ByVal backBrush As System.Drawing.Brush, _
              ByVal foreBrush As System.Drawing.Brush, _
              ByVal alignToRight As Boolean)
        Dim evArgs As RowColorEventArgs
        ' Guardamos las brochas para su posterior restauración.
        Dim oldBackBrush As Brush = backBrush
        Dim oldForeBrush As Brush = foreBrush
 
        ' Pedimos el color de fondo
        evArgs = New RowColorEventArgs(Me.TextBox.BackColor, source, rowNum)
        RaiseEvent GetBackColor(Me, evArgs)
 
        ' Creamos la brocha de fondo.
        backBrush = New SolidBrush(evArgs.RowColor)
 
        ' Pedimos el color del texto.
        evArgs = New RowColorEventArgs(Me.TextBox.ForeColor, source, rowNum)
        RaiseEvent GetForeColor(Me, evArgs)
 
        ' Creamos la brocha del texto
        foreBrush = New SolidBrush(evArgs.RowColor)
 
        ' Pintamos
        MyBase.Paint(g, bounds, source, rowNum, _
                     backBrush, foreBrush, alignToRight)
 
        ' Liberamos las brochas! Los recursos de GDI son limitados!
        backBrush.Dispose()
        foreBrush.Dispose()
 
        ' Restauramos las brochas antiguas
        backBrush = oldBackBrush
        foreBrush = oldForeBrush
    End Sub
End Class

La verdad es que no estoy satisfecho con la solución. La cantidad de eventos generados para pintar el DataGrid es enorme. De todas formas, el código para utilizar esta clase, es un poco truculento, y es el que pongo a continuación:

    ' En Form1.vb...
 
    Private Sub Form1_Load(ByVal sender As System.Object, _
             ByVal e As System.EventArgs) Handles MyBase.Load
        Dim tableStyle As New DataGridTableStyle
        tableStyle.MappingName = Me.OrdersDataSet1.Orders.TableName
 
        Me.FillDataSet()
 
        For Each col As DataColumn In Me.OrdersDataSet1.Orders.Columns
            Dim gridCol As DataGridColoredTextBoxColumn = _
                               New DataGridColoredTextBoxColumn
 
            gridCol.MappingName = col.ColumnName
            gridCol.HeaderText = col.ColumnName
 
            AddHandler gridCol.GetBackColor, AddressOf Me.GetBackColor
            tableStyle.GridColumnStyles.Add(gridCol)
        Next
 
        DataGrid1.TableStyles.Add(tableStyle)
        DataGrid1.DataSource = Me.OrdersDataSet1.Orders
 
    End Sub
 
    Private Sub GetBackColor(ByVal sender As Object, _
                             ByVal e As RowColorEventArgs)
        Dim data As DataRowView
        Dim value As Integer
 
        data = CType(e.Source.List.Item(e.RowIndex), DataRowView)
 
        value = CInt(data("EmployeeID"))
 
        If value = 5 Then
            e.RowColor = Color.Red
        End If
    End Sub
 
    Private Sub FillDataSet()
        ' ...
    End Sub

Estoy seguro de que hay una forma más sencilla de hacerlo. ¿Alguien conoce alguna?

11 comentarios en “DataGrid de .NET 1.1 – Cambiando el estilo a las filas”

  1. Hola shadow,

    Para colorear una celda que se escoja con el puntero (supongo que haciendo click sobre ella) lo que puedes utilizar es lo siguiente:

    Añadir un constructor a la clase DataGridColoredTextBoxColumn en el que le pasas el DataGridTableStyle que estás utilizando en el DataGrid:

    Private tableStyle As DataGridTableStyle

    Public Sub New(ByVal style As DataGridTableStyle)
    tableStyle = style
    End Sub

    Hay que cambiar el método Paint de esta clase, haciendo que el Color que utiliza para levantar los eventos los saque del objeto TableStyle que le pasas al constructor. Para ello, añades estas dos variables en el método:

    Dim backColor As Color
    Dim foreColor As Color

    Y haces lo siguiente al inicio del método:

    foreColor = tableStyle.ForeColor

    ‘ Es impar?
    If (rowNum And 1 = 0) Then
    backColor = tableStyle.BackColor
    Else
    backColor = tableStyle.AlternatingBackColor
    End If

    En la creación de las instancias de RowColorEventArgs, le pasas los colores backColor y foreColor en vez de utilizar los colores del TextBox:

    ‘ Pedimos el color de fondo
    evArgs = New RowColorEventArgs(backColor, source, rowNum)
    RaiseEvent GetBackColor(Me, evArgs)

    Y lo mismo para el evento GetForeColor.

    Ahora, en el formulario hemos de hacer lo siguiente:

    En el evento Load, al crear los objetos DataGridColoredTextBoxColumn hemos de pasar el DataGridTableStyle que estamos utilizando. Para ello cambiamos la línea de la declaración de la variable por la siguiente:

    Dim gridCol As DataGridColoredTextBoxColumn = New DataGridColoredTextBoxColumn(tableStyle)

    Añadimos las siguientes variables privadas al formulario:

    Dim currentRow As Integer = -1
    Dim currentCol As Integer = -1

    La gestión del evento GetBackColor sería la siguiente:

    Private Sub GetBackColor(ByVal sender As Object, ByVal e As RowColorEventArgs)
    Dim data As DataRowView
    Dim value As Integer
    Dim col As DataGridColoredTextBoxColumn = CType(sender, DataGridColoredTextBoxColumn)
    Dim colIndex As Integer

    colIndex = DataGrid1.TableStyles(Me.OrdersDataSet1.Orders.TableName).GridColumnStyles.IndexOf(col)

    data = CType(e.Source.List.Item(e.RowIndex), DataRowView)

    If Not data(“EmployeeID”) Is System.DBNull.Value Then
    value = CInt(data(“EmployeeID”))

    If value = 5 Then
    e.RowColor = Color.Red
    End If
    End If

    If (colIndex = currentCol AndAlso e.RowIndex = currentRow) Then
    e.RowColor = Color.Blue
    col.TextBox.BackColor = Color.Blue
    End If

    End Sub

    Y por último, hemos de gestionar el evento MouseDown del DataGrid, para anotar la fila y la columna en la que hemos hecho click con el ratón:

    Private Sub DataGrid1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DataGrid1.MouseDown
    Dim htInfo As DataGrid.HitTestInfo = DataGrid1.HitTest(e.Location)

    If htInfo.Type = DataGrid.HitTestType.Cell Then
    currentRow = htInfo.Row
    currentCol = htInfo.Column
    End If
    End Sub

    Espero que te sirva 😉

  2. Jur… cuando he leido lo de que en la version 2.0 de la framework se puede hacer de una manera mucho mas sencilla me estan entrando ganas de cambiar el proyecto…. Pero mejor probare antes la opcion del 1.1, no vaya a ser que la fastidie por completo.

    Por cierto nene, si pongo una secicon de agradecimientos en el proyecto, te incluire junto con un 10% de los beneficios que saque de la empresa a la que se lo estoy haciendo!!!! (10% de 0€… bueno, algo ya es xDDDDDDD)

  3. Me basta con que te sirva 😉

    No sé a qué altura estás del proyecto, pero si quieres puedes hacer una prueba de migración para ver si se te descacharraría del todo.

    Bastaría con que hagas una copia de la carpeta donde tienes el proyecto (aunque el wizard de migración pregunta si quieres que haga un backup del proyecto original), y abrir la solución con el Visual Studio 2005. Al finalizar, puedes ver el informe de la migración con los avisos y errores que puedan haber surgido, aunque en general, si el proyecto no es muy complicado, no suele haber ningún problema.

    Eso sí, todo depende de la altura a la que estés, de si te merece la pena migrar, si tu tutor está de acuerdo, etc, etc, etc…

    Suerte con el proyecto!

  4. Hola Toni,

    OrdersDataSet es un dataset tipado creado a partir de la tabla Orders de la base de datos de ejemplo Northwind (creo recordar que era esa la que usé, pero no estoy seguro al 100%).

    En el adjunto debería estar la definición de dicha clase.

    Saludos!

  5. Yo he encontrado un monton de paginas relacionada a esto.
    y esta esta muy buena. pero mi intencion es enviar los datos del datagrid a excel o pdf etc. de esto tambien hay bastante. pero lo que no encuenmtro y no se halla en ninguna pagina es como le paso el formato del datagrid al excel pdf etc. es decir el datagridtablestyle.

  6. Respuestas a las preguntas:

    @Pregunta:

    El código no es compatible con el framework 2.0 de .NET a no ser que estés utilizando el DataGrid de .NET 1.1. Sin embargo, para el DataGridView de .NET 2.0, el cambio de estilo de las filas es mucho más sencillo, ya que a partir de un DataGridViewRow (que es lo que te devuelve la propiedad Rows, o la propiedad SelectedRows del DataGridView) puedes acceder al estilo de la misma:

    miDataGridView.Rows(i).DefaultCellStyle

    @Jimmy:

    Para pasarle el estilo a Excel no existe un método directo. Por lo tanto, me temo que tendrás que jugar con el modelo de objetos de Excel (y dependerá de la versión contra la que estés trabajando) para poder darle estilo.

    Un consejo: Cuando tengas que utilizar el modelo de objetos de Excel y no sepas hacer algo con ello, hay un “truco” muy sencillo: Abre el Excel, graba una macro que haga lo que necesitas hacer, y examina el código que genera. Te servirá de base para implementar esa funcionalidad que se te resiste.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *