.NET, Trucos, Windows Forms

Crear una columna de botones con imagen usando la clase DataGridViewButtonColumn

Cuando presentamos un conjunto de datos en forma tabular mediante el control DataGridView, hay ocasiones en las que se necesita proporcionar al usuario la posibilidad de realizar ciertas operaciones, en base a uno o varios valores situados en las celdas de una determinada fila del control.

Por ejemplo, en el formulario que vemos en la siguiente imagen se muestra un DataGridView con algunos campos de la tabla DimProduct, perteneciente a la base de datos AdventureWorksDW. El objetivo consiste en que el usuario pueda introducir en el TextBox un valor, que representa un porcentaje de descuento a aplicar sobre el campo ListPrice, de la fila del DataGridView que seleccione.

CrearColumnaBotonesImagenUsandoClaseDataGridViewButtonColumn_01

 

Entre las diversas técnicas disponibles para realizar la operación que acabamos de mencionar, en este artículo vamos a hacer uso de las clases DataGridViewButtonColumn y DataGridViewButtonCell, que combinaremos para crear, dentro del control DataGridView, una nueva columna calculada, cuyas celdas contengan botones, que al ser pulsados, nos permitan realizar una operación con los valores de otras celdas de la fila seleccionada. Como particularidad añadida, los botones de nuestra nueva columna contendrán una imagen.

En primer lugar crearemos, en Visual Studio 2008, un nuevo proyecto de tipo Windows Forms Application, al que daremos el nombre DGVColumnaBotonesImagen, añadiendo al diseñador del formulario los controles mostrados en la anterior imagen.

Seguidamente pasaremos al editor de código del formulario, y en su evento Load escribiremos el siguiente bloque de código, que usaremos para conectarnos a una base de datos y cargar el DataGridView con un subconjunto de los registros de la tabla.

using System.Data.SqlClient;
//....
namespace DGVColumnaBotonesImagen
{
public partial class Form1 : Form
{
    //....

    private void Form1_Load(object sender, EventArgs e)
    {
        SqlConnection cnConexion = new SqlConnection();
        cnConexion.ConnectionString = "Data Source=localhost;" +
            "Initial Catalog=AdventureWorksDW;" +
            "Integrated Security=True";

        string sSQL = "SELECT ProductKey, SpanishProductName, ListPrice " +
            "FROM DimProduct WHERE ListPrice IS NOT NULL";

        SqlCommand cmdComando = new SqlCommand(sSQL, cnConexion);
        SqlDataAdapter daAdaptador = new SqlDataAdapter(cmdComando);
        DataTable tblDimProduct = new DataTable();
        daAdaptador.Fill(tblDimProduct);
        this.dataGridView1.DataSource = tblDimProduct;
        //....
    }
    //....

A continuación definiremos nuestra columna personalizada creando una instancia de la clase DataGridViewButtonColumn, que añadiremos a la colección de columnas del DataGridView.

private void Form1_Load(object sender, EventArgs e)
{
    //....
    DataGridViewButtonColumn colBotones = new DataGridViewButtonColumn();
    colBotones.Name = "colBotones";
    colBotones.HeaderText = "Valor Stock";

    this.dataGridView1.Columns.Add(colBotones);
}

La siguiente imagen muestra el resultado.

CrearColumnaBotonesImagenUsandoClaseDataGridViewButtonColumn_02

 

El anterior código crea una nueva columna basada en botones, pero estos carecen de contenido. Para incluir una imagen en su interior crearemos un manipulador para el evento CellPainting del control, en el que realizaremos las siguientes operaciones:

En primer lugar comprobaremos si la celda a pintar corresponde a nuestra columna de botones y se encuentra en una fila válida; en caso afirmativo, pintamos la celda ejecutando el método DataGridViewCellPaintingEventArgs.Paint. Seguidamente obtenemos una instancia del botón situado en la celda y otra de la imagen (un icono en este ejemplo) a situar en su interior, dibujando esta mediante el método Graphics.DrawIcon, con un tamaño ligeramente inferior al del botón.

Finalmente, para poder visualizar de forma más adecuada el botón, ajustamos las dimensiones de la fila y columna que lo alojan en función de las dimensiones de la imagen. También debemos asignar el valor true a la propiedad DataGridViewCellPaintingEventArgs.Handled, que nos servirá para que el control tenga en cuenta la implementación que hemos realizado sobre este evento.

private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
    if (e.ColumnIndex >= 0 && this.dataGridView1.Columns[e.ColumnIndex].Name == "colBotones" && e.RowIndex >= 0)
    {
        e.Paint(e.CellBounds, DataGridViewPaintParts.All);

        DataGridViewButtonCell celBoton = this.dataGridView1.Rows[e.RowIndex].Cells["colBotones"] as DataGridViewButtonCell;
        Icon icoAtomico = new Icon(Environment.CurrentDirectory + @"Atomico.ico");
        e.Graphics.DrawIcon(icoAtomico, e.CellBounds.Left + 3, e.CellBounds.Top + 3);

        this.dataGridView1.Rows[e.RowIndex].Height = icoAtomico.Height + 10;
        this.dataGridView1.Columns[e.ColumnIndex].Width = icoAtomico.Width + 10;

        e.Handled = true;
    }
}

En la siguiente imagen podemos ver el control con la columna resultante, incluyendo los botones con el icono.

CrearColumnaBotonesImagenUsandoClaseDataGridViewButtonColumn_03

 

Tan sólo resta añadir al botón la lógica que efectúe el cálculo del descuento comentado al comienzo del artículo, aspecto este que resolveremos codificando el evento CellClick del DataGridView. Al producirse este evento comprobaremos si existe valor en el TextBox del formulario, y en caso afirmativo, nos cercioraremos de que la celda sobre la que se ha realizado la pulsación corresponde a nuestra columna calculada, si esto se cumple, procederemos con el cálculo, mostrándolo en una caja de mensaje.

private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
    if (this.txtPorcentaje.Text == string.Empty)
    {
        MessageBox.Show("Introducir valor para el porcentaje");
        this.txtPorcentaje.Focus();
    }
    else
    {
        if (this.dataGridView1.Columns[e.ColumnIndex].Name == "colBotones")
        {
            decimal nListPrice = (decimal)this.dataGridView1.Rows[e.RowIndex].Cells["ListPrice"].Value;
            int nPorcentaje = int.Parse(this.txtPorcentaje.Text);
            decimal nDescuento = (nListPrice * nPorcentaje) / 100;

            MessageBox.Show(nDescuento.ToString("#,#.##"), "Descuento aplicable");
        }
    }
}

La siguiente imagen muestra el resultado de una operación.

CrearColumnaBotonesImagenUsandoClaseDataGridViewButtonColumn_04

 

Utilizando el control DataGridView, en este artículo hemos explicado una técnica, que de manera sencilla nos permite crear una columna conteniendo botones que muestran un icono en su interior, lo cual nos puede servir para dar una mejor apariencia a nuestros controles de cuadrícula. Espero que os resulte de utilidad.

Un saludo.

15 Comentarios

  1. anonymous

    Crear para el control DataGridView, una columna cuyas celdas contengan botones, los cuales realicen una

  2. anonymous

    hola muchas gracias la verdad me ha servido mucho tu ejemplo, es justo lo que buscaba, nada más que me gustaría saber si en lugar de poner un icono, podria yo poner texto yo lo quiero para que el botón diga ‘Edit’ ok?
    te agradeceria que me dijeras como
    muchas gracias de antemano.

  3. lmblanco

    Hola Danny

    Puedes utilizar el método Graphics.DrawString para dibujar un texto dentro del botón, de forma similar a como puedes ver en el siguiente bloque de código:

    //———————————–
    private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
    {
    if (e.ColumnIndex >= 0 && this.dataGridView1.Columns[e.ColumnIndex].Name == “colBotones” && e.RowIndex >= 0)
    {
    e.Paint(e.CellBounds, DataGridViewPaintParts.All);

    DataGridViewButtonCell celBoton = this.dataGridView1.Rows[e.RowIndex].Cells[“colBotones”] as DataGridViewButtonCell;

    e.Graphics.DrawString(“Edit”, new Font(“Verdana”,8),new SolidBrush(Color.Black),
    e.CellBounds.Left + 3, e.CellBounds.Top + 3);

    e.Handled = true;
    }
    }
    //———————————–

    Un saludo.
    Luismi

  4. anonymous

    Hola Luismi,

    gracias por el artículo, me ha sido muy útil. Yo lo que estoy haciendo es una columna con botones ( en un datagrid no asociado a un datasource, sólo son 4 filas ) con un botón con un texto ( de momento, quizá en un futuro va una imagen ). El tema es que en una de las filas no quiero mostrar el botón. Cómo lo podria ocultar?

    Muchas gracias,

    Vicenç

  5. lmblanco

    Hola Vicenç

    Gracias por tu opinión y por el interés en el artículo. Sobre tu comentario acerca de ocultar un botón, hay que tener en cuenta que cuando añadimos a un DataGridView una columna de tipo DataGridViewButtonColumn, al ejecutar la aplicación, el DataGridView se mostrará con esa columna llena de botones, aunque estén vacíos, sin imagen o texto, ya que el comportamiento base de la clase DataGridViewButtonColumn es la creación de dicha columna rellena de botones, luego tú los puedes personalizar como quieras: añadiendo texto o imagen como se explica en el artículo del blog.

    Si quieres ir más allá en la personalización de una columna de estas características, es posible que tengas que utilizar algo similar a lo que se explica en la serie de artículos que puedes encontrar en el siguiente enlace:

    http://geeks.ms/blogs/lmblanco/archive/2009/09/19/datagridviewcolumn-y-datagridviewcell-creaci-243-n-de-columnas-personalizadas-para-el-control-datagridview-1.aspx

    Aquí se explica el modo de creación de una columna y celdas partiendo de cero, requiere más trabajo pero es más personalizable.

    Espero que te resulte de utilidad.

    Un saludo.
    Luismi

  6. dcc

    Links caidos para descargar el proyecto.

    ;(

    • lmblanco

      Hola dcc

      Siento mucho que no te haya funcionado el enlace para la descarga, desde que se hizo la migración de los blogs a la nueva plataforma estoy evaluando alguna alternativa para poner estos ficheros disponibles.

      Gracias y un saludo,
      Luismi

  7. en visual basic seria asi
    If (dgvItems.Columns(e.ColumnIndex).Name = “huesped_boton”) Then

    obj.ShowDialog()
    End If

  8. lmblanco

    Hola Daniel

    OK, muchas gracias por tu aportación. 🙂

    Un saludo.
    Luismi

  9. María

    Lástima, no se ven las imágenes.

    • lmblanco

      Hola María

      Disculpa el problema con las imágenes del artículo, hace unas semanas que se realizó una migración de los blogs a WordPress y estoy en pleno proceso de restaurar las que no se visualizan. En este ya están restauradas.

      Muchas gracias por tu interés en el blog 🙂

      Un saludo,
      Luismi

  10. Rod

    Está muy bueno el ejemplo
    Yo necesito que el botón sea del tamaño de la mitad o menos de la celda que lo contiene, asimismo, que a la misma vez la celda permita introducir texto, y que al presionar el botón tenga un evento que se pueda abrir un formulario o ejecutar código.
    Me podrías ayudar con eso?
    Gracuas

  11. Adal

    Muchas gracias, excelente!

  12. lmblanco

    Hola Adal

    Gracias por tu opinión, celebro que te haya servido de ayuda.

    Un saludo,
    Luismi

Leave a Reply

Tema creado por Anders Norén