Formatear y resaltar valores en el control DataGridView con el evento CellFormatting (II)

Siguiendo con los aspectos relacionados con el formateo de valores utilizando este evento, en la presente entrega abordaremos la posibilidad de aplicar formatos basados en una condición y sobre una columna a partir de los valores existentes en otras.


 


Formato condicional


Las operaciones tratadas hasta el momento podríamos haberlas realizado prescindiendo del evento CellFormatting, ya que al aplicar el formato a la totalidad de la columna, habría bastado con seleccionar de la colección DataGridView.Columns aquella columna a formatear, y mediante la propiedad DefaultCellStyle -que contiene un tipo DataGridViewCellStyle-asignar a la propiedad Format la cadena de formato adecuada. Esto podemos hacerlo, por ejemplo, en el evento Load.


private void frmFormatoCondicional_Load(object sender, EventArgs e)
{
this.dataGridView1.DataSource = ConstruirDatos.ObtenerTabla();
this.dataGridView1.Columns[«BirthDate»].DefaultCellStyle.Format = «MMMM (yyyy)»;
}


Es por ello que un uso más conveniente del evento CellFormatting consistiría en utilizarlo para formatear las celdas de una columna que cumplan una determinada condición, como podemos ver en el siguiente bloque de código, donde tras verificar que para la columna de tipo fecha, la celda a formatear contiene valor, si este corresponde al mes de febrero, se le aplica una cadena de formato para resaltar dicha celda.


private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (this.dataGridView1.Columns[e.ColumnIndex].Name == «BirthDate»)
{
try
{
// comprobar si la celda tiene contenido válido
if (e.Value.GetType() != typeof(System.DBNull))
{
// si la celda tiene valor y el mes es febrero
// formatear el contenido
if (((DateTime)e.Value).Month == 2)
{
e.CellStyle.Format = «D»;
e.CellStyle.BackColor = Color.Aqua;
}
}
}
catch (NullReferenceException oException)
{
}
}
}

 

A continuación apreciamos el resultado en la siguiente imagen.


 


Formato condicional aplicado a la totalidad de la columna


En ciertas ocasiones puede resultar interesante aplicar una operación de formato de modo global a todos los valores de una misma columna, consistente por ejemplo en modificar el texto original por otro más extenso, como podemos ver en el siguiente código, donde sustituimos los valores que inicialmente provienen de la columna Title de la tabla de datos.


private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
// cambiar el valor del campo Title
if ((this.dataGridView1.Columns[e.ColumnIndex].Name == «Title») && (e.Value != null))
{
string TituloCortesia = string.Empty;

switch (e.Value.ToString())
{
case «Sr.»:
case «Mr.»:
TituloCortesia = «Señor»;
break;

case «Mrs.»:
TituloCortesia = «Señora»;
break;

case «Ms.»:
case «Ms»:
TituloCortesia = «Señorita»;
break;
}

e.Value = TituloCortesia;
}
}


Debido a la naturaleza del formato a realizar, este no puede acometerse asignando valores a los miembros de la propiedad DefaultCellStyle, perteneciente a la colección DataGridView.Columns -como veíamos en un caso anterior. Dado que los cambios necesarios no siguen un patrón prefijado, resulta necesario resolver el problema desde el flujo del código, dentro del  evento CellFormatting.



 


Formato indirecto


Como acabamos de comprobar, el formato condicional resulta de utilidad cuando deseamos aplicar un formato a las celdas de una columna en base a una expresión evaluada contra los valores de dicha columna. Sin embargo también podemos aplicar una variante de esta técnica que consistiría en realizar el formato sobre una celda, pero esta vez en función de la información proveniente de celdas pertenecientes a otras columnas.


Pongamos como ejemplo, que en los datos con los que estamos operando, necesitamos destacar el nombre de los clientes cuyos ingresos anuales superen una determinada cantidad. Para ello, dentro del evento CellFormatting, cuando corresponda aplicar formato a las columnas FirstName y LastName, comprobaremos el valor de la celda correspondiente a la columna YearlyIncome, y según sea su valor, resaltaremos o no las celdas del nombre. Todo esto podemos comprobarlo en el siguiente código.


private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
// si la celda a formatear corresponde al nombre del cliente…
if ((this.dataGridView1.Columns[e.ColumnIndex].Name == «FirstName») ||
(this.dataGridView1.Columns[e.ColumnIndex].Name == «LastName»))
{
//…y sus ingresos superan una cierta cantidad,
// formatear las celdas del nombre
if ((decimal)this.dataGridView1.Rows[e.RowIndex].Cells[«YearlyIncome»].Value >= 100000)
{
e.CellStyle.BackColor = Color.PaleGreen;
e.Value = e.Value.ToString().ToUpper();
}
}
}

La siguiente imagen muestra el resultado de este tipo de formato.



 


Formato indirecto utilizando una columna con imágenes


Una variante de la técnica ofrecida en el apartado anterior consistiría en utilizar una columna de tipo imagen -DataGridViewImageColumn- para mostrar una figura que represente el valor de una columna. Dentro de la tabla que estamos usando para nuestros ejemplos, un claro candidato lo representaría la columna Gender, que nos informa acerca del género -masculino o femenino-correspondiente al cliente.


A efectos visuales, dicha columna podemos sustituirla por una de imagen, que muestre una figura identificativa, pero sin eliminar la columna Gender -que quedaría oculta-, ya que necesitaremos recurrir a ella para consultar su valor y saber qué imagen situar en la celda en curso.


En primer lugar agregaremos al proyecto sendos archivos de tipo imagen, que configuraremos en su propiedad Build Action para que se comporten como recursos incrustados -Embedded Resource.



A continuación pasaremos al código del formulario, donde en el evento Load ocultaremos la columna Gender y crearemos una nueva para contener las imágenes. Por otro lado, en el evento CellFormatting detectaremos cuando debe ser dibujada la celda de la nueva columna de imagen, y en dicho momento, comprobaremos el valor de la columna oculta Gender, asignando la imagen correcta. Todas estas operaciones las podemos ver en el siguiente código fuente.


private void frmFormatoIndirectoImagen_Load(object sender, EventArgs e)
{
this.dataGridView1.DataSource = ConstruirDatos.ObtenerTabla();

// ocultar la columna que indica el género del cliente
this.dataGridView1.Columns[«Gender»].Visible = false;

// crear una nueva columna de tipo imagen
// para mostrar un icono indicativo del género del cliente
// y agregarla a la colección de columnas del control
DataGridViewImageColumn colGenero = new DataGridViewImageColumn();
colGenero.Name = «colGenero»;
this.dataGridView1.Columns.Add(colGenero);
}

private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
// cuando se vaya a pintar la celda de la columna de imagen
if (this.dataGridView1.Columns[e.ColumnIndex].Name == «colGenero»)
{
// establecer la altura de la fila para que se pueda visualizar correctamente
this.dataGridView1.Rows[e.RowIndex].Height = 40;

// asignar la imagen adecuada
switch ((string)this.dataGridView1.Rows[e.RowIndex].Cells[«Gender»].Value)
{
case «M»:
e.Value = new Bitmap(this.GetType(), «SimboloMasculino.jpg»);
break;

case «F»:
e.Value = new Bitmap(this.GetType(), «SimboloFemenino.jpg»);
break;
}
}
}


En la siguiente imagen podemos observar el efecto de esta nueva columna en tiempo de ejecución. Como dato curioso, el valor del campo Gender en la tabla de la base de datos no se corresponde correctamente en todas las ocasiones.



La próxima entrega, que finaliza esta serie de artículos, abordará algunos aspectos adicionales de este tipo de operaciones. Como ya comentamos en la primera parte, los ejemplos están disponibles en estos enlaces: C# y VB.


Un saludo.


 

15 Comentarios

  1. lmblanco

    Hola Enrique

    Muchas gracias, me alegra de que te hayan parecido interesantes 8-).

    Un saludo.
    Luismi

  2. anonymous

    Hola creo que tienes un error en el primer ejemplo (de las fechas)

    if (e.Value == null || e.Value != System.DBNull.Value)

    deberia ser e.Value != null..

    Saludos.. por cierto muy buen material

  3. lmblanco

    Hola Alejo

    Muchas gracias por el aviso, en efecto, hay un error en la línea que comentas, pero el uso de

    e.Value != null

    tampoco soluciona el problema cuando nos encontramos en un registro cuyo campo BirthDate es nulo. Acabo de revisar este código y para que funcione correctamente hemos de utilizar la siguiente línea:

    if (e.Value.GetType() != typeof(System.DBNull))

    De esta forma sí que podemos averiguar si el campo de fecha está a nulo, para proceder a darle o no formato.

    Gracias de nuevo por tu indicación y por tu opinión sobre el post 8-).

    Un saludo.
    Luismi

  4. anonymous

    Hola Comunidad …
    Tengo un problema con un columntype imagen, en la ultima fila adicionada me sale el icono de error de caragado de imagen , en todas las demas fila si me carga perfectamente la imagen pero en la ultima me da me sale ese error sin embargo puedo introducrile bien los datos

  5. anonymous

    Hola Orlando

    Deberás detectar cuándo se produce la situación de final de fila en el DataGridView, para obligar a que muestre la imagen en la celda.

    También puedes utilizar la siguiente dirección de los newsgroups de C# para formular tu consulta, donde seguramente recibirás ayuda adicional:

    http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?pg=1&cat=&lang=&cr=&guid=&sloc=en-us&dg=microsoft.public.es.csharp&fltr=

    Un saludo.
    Luismi

  6. anonymous

    Felicidades por tus aportaciones, me sirvieron mucho.

    Monterrey México

  7. lmblanco

    Hola Arturo

    Muchas gracias por tu opinión y por leer el artículo; celebro que te sirviera de ayuda 😎

    Un saludo,
    Luismi

  8. anonymous

    Felicitaciones por los ejemplos (me ayudo mucho)

  9. lmblanco

    Hola LuisNet

    Gracias por tu interés en el post, me alegra que te haya servido.

    Un saludo,
    Luismi

  10. anonymous

    Muy buena ayuda, me has sacado de un dilema, tenia muchos problemas para lograr mostrar una imagen diferente por cada registro de la tabla, anduve probando con el evento RowsAdded pero sin obtener los resultados que esperaba pero con esta guia estupenda lo he podido hacer en un dos por tres, muchas gracias.

  11. lmblanco

    Hola Antonio

    Gracias por leer el post y celebro que te haya servido de ayuda.

    Un saludo.
    Luismi

  12. anonymous

    Muchas gracias por tu trabajo.

    Aun así tengo un problemilla. Tengo una celda tipo checkbox, enlazada a un campo en la base de datos. Los valores que puede tomar son 4 que sería Sí (true) y 5 que sería NO (false).

    Como las dos son distintos de 0 siempre me pone la celda como checked. Por ejemplo, el DataGridViewCell.Value = 5, pero el DataGridViewCell.FormattedValue es True, ya que el DataGridViewCell.FormattedValueType = Boolean

    ¿Se te ocurre la forma de poder controlar que si el valor es 4 se ponga Checked y si es 5 Unchecked?

    muchas gracias

  13. lmblanco

    Hola Julio

    En la consulta SQL que utilizas para posteriormente enlazar con el datagridview, puedes probar a crear el campo que empleas para esa columna checkbox como un campo calculado, que en función del valor original del campo, te convierta a 0 ó 1. Algo parecido a lo siguiente:

    SELECT CAMPO1, CAMPO2,
    CASE
    WHEN CAMPO3 = 4 THEN 1
    WHEN CAMPO3 = 5 THEN 0
    END AS CAMPO3
    FROM NOMBRE_TABLA

    De esta forma, cuando enlaces con la columna checkbox del datagridview, te debería visualizar las celdas marcadas y desmarcadas correctamente.

    Un saludo,
    Luismi

  14. anonymous

    Muy buen aporte, hay muchos ejemplos buenos

  15. lmblanco

    Hola Manuel

    Gracias por tu interés en el artículo.

    Un saludo,
    Luismi

Deja un comentario

Tema creado por Anders Norén