Customize GridViewColumn: Personalizando columnas de gridview por código con controles

Muchas veces he tenido la necesidad de incluir información concreta en una columna de un GridView, y me ha costado mucho encontrar referencias que explicaran en profundidad cómo realizar la “customización” de una columna (sobre todo en castellano, porque no encontraba documentación al respecto).

En este post, vamos a explicar paso a paso cómo se puede personalizar completamente una columna de un GridView por código. En concreto, lo que realizaremos una personalización de una columna con un CheckBox  (que permita su edición), y una cabecera que seleccione todas las filas del grid.

Para ello debemos crear  una plantilla para la columna y una vez realizada, referenciarla desde la página del CodeBehind, indicándole así al grid cómo debe mostrar la información.

Dado que sería muy largo, lo he dividido en dos partes:
Parte I: Generación de la plantilla
    PASO 1: Generación de la plantilla ITemplate
    PASO 2: Control de eventos
Parte II: Construcción del GridView


PREVIO:
En este ejemplo, vamos a construir una columna de  CheckBox que controla un listado de test realizados por usuarios. El nombre de cada columna hará referencia al campo equivalente en la DDBB (así, Test1 sería la columna de los datos para el Test1). Además vamos a incluir en la cabecera una imagen que al pulsar, seleccionará todos los checks del gridview.


Parte I: Generación de la plantilla

PASO 1: Generación de la clase ITemplate
Para poder referenciar nuestra columna desde el GridView, necesitamos proporcionarle al sistema una forma de generar nuestra columna de CheckBox. Para ello hay que crear una clase que implemente el interfaz ITemplate. En nuestro ejemplo, denominaremos a la clase GridViewCheckboxTemplate.

public class GridViewCheckboxTemplate : ITemplate
{
    //Event handler - boton Header para “Seleccionar Todos”
    public event EventHandler ImageClick;

    //atributo para almacenar el ListItemType.
    ListItemType _templateType;

    //Atributo con el nombre identificador de la columna.
    private string _columnName;

    //Constructor donde definimos el tipo y nombre.
    public GridViewCheckboxTemplate(ListItemType type, string colname)
    {
        _templateType = type;
        _columnName = colname;
    }
 }

El ListItemType es el que nos indicará que parte del GridViewColumn estamos editando. Los valores que puede tomar son Header, Item, EditItem y Footer.

NOTA: En este caso, estamos generando una columna a partir de un constructor con 2 parametros. Podríamos crear otros constructores con más parámetros, en función de las necesidades.

Una vez implementados los atributos y el constructor, debemos indicar al sistema cómo ha de generar la vista de la columna en el GridView. Para ello, implementaremos el método ITemplate.InstantiateIn:

void ITemplate.InstantiateIn(System.Web.UI.Control container)
{
    switch (_templateType)
    {
        case ListItemType.Header:
            //TODO – elementos de la cabecera.
            break;
        case ListItemType.Item:
            //TODO – CheckBox en formato PRESENTACIÓN.
            break;
        case ListItemType.EditItem:
            //TODO –  CheckBox en formato EDICIÓN.
            break;
        case ListItemType.Footer:
            //TODO – elementos del footer.
            break;
    }
}

En la definición del método anterior podemos observar que, en función del tipo de ítem que estemos solicitando, el sistema generará unos controles u otros para el render de la columna completa. Por tanto, ya podemos incluir los controles necesarios para nuestra personalización:

HEADER:

Vamos a incluir un control ImageButton, de forma que aparezca un selector multiple que actualice todas las filas, y un Label con el nombre de la fila: 

case ListItemType.Header:
    ImageButton imgButton = new ImageButton();
    imgButton.ID = "imgHeader_" + _columnName;
    imgButton.ImageUrl = "~/images/ico/check_all.png";
    imgButton.ImageAlign = ImageAlign.AbsMiddle;
    imgButton.ToolTip = "Activar TODOS ";
    imgButton.Click += new ImageClickEventHandler(imgHeader_ClickEventHandler);
    imgButton.Attributes.Add("onclick", "return confirm('¿Seguro que desea activar todas las filas?');");
    container.Controls.Add(imgButton);

    //Creamos el label que indica en qué columna estamos.
    Label lbl = new Label();
    lbl.Text = " "+_columnName;
    container.Controls.Add(lbl);
    break;

Como se puede observar, para cada control establecemos todas sus propiedades, entre las que cabe destacar:
· La Url relativa a la raíz del proyecto la imagen que vamos a mostrar:
    imgButton.ImageUrl = "~/images/ico/check_all.png";  ->
· El evento que se disparará cuando se pulse sobre la imagen. Más adelante veremos cómo programar esta función:
        imgButton.Click += new ImageClickEventHandler(imgHeader_ClickEventHandler);
· Una confirmación de activación para todos los elementos:
        imgButton.Attributes.Add("onclick", "return confirm('¿Seguro que desea activar todas las filas?');");

ITEM y EDITITEM:
Como sólo vamos a mostrar un checkBox, simplemente debemos añadir dicho control a la columna:

case ListItemType.Item:
    //Creamos el checkBox.
    CheckBox chk = new CheckBox();
    chk.EnableViewState = true;
    chk.Enabled = false; //en vista, NO PERMITIMOS la modificación del valor.
    chk.ID = "chkItem_" + _columnName;
    chk.DataBinding += new EventHandler(CB1_DataBinding); //enlace a datos.
    container.Controls.Add(chk);
    break;

case ListItemType.EditItem:
    // Creamos el checkbox para edición
    CheckBox chkEdit = new CheckBox();
    chkEdit.EnableViewState = true;
    chkEdit.Enabled = true; //en edición, PERMITIMOS la modificación del valor
    chkEdit.ID = "chkEditItem_" + _columnName;
    chkEdit.DataBinding += new EventHandler(CB1_DataBinding);   //enlace a datos.
    container.Controls.Add(chkEdit);
    break;

A destacar el DataBinding que se encargará de mostrar la información al generar el GridView (ver paso 2 ‘Control de Eventos’), y la opción Enabled que es la que permite la modificación del estado del checkbox.

FOOTER:
En este ejemplo, no añadimos ningún elemento al footer, pero se podría personalizar con cualquier control que necesitaramos. Como ejemplos; un botón para añadir un nuevo ítem, ó un Label con información del total de registros, o ubicar aquí el selector multiple, etc.


NOTA: Recordar que si incluimos aquí cualquier control, en vista de diseño deberemos marcar el atributo ShowFooter a true.

PASO 2: Control de eventos
Una vez generado el método para construir la columna debemos controlar los eventos de datos y, en este ejemplo, del botón header para ‘Seleccionar todos’.

Control del click sobre la imagen
    void imgHeader_ClickEventHandler(object sender, ImageClickEventArgs e)
    {
        if (ImageClick != null) { ImageClick(sender, EventArgs.Empty); }
    }
Aquí simplemente verificamos que existe un EventHandler asociado a la imagen y en caso afirmativo, llamamos al delegado. Éste será el encargado de modificar los datos y actualizar el grid.

Control de los datos a mostrar
    void CB1_DataBinding(object sender, EventArgs e)
    {
        CheckBox chk = (CheckBox)sender; //Obtenemos el control
        GridViewRow container = (GridViewRow)chk.NamingContainer; //contenedor con datos
        object dataValue = DataBinder.Eval(container.DataItem, _columnName); //valor obtenido
        chk.Checked = (dataValue.ToString() == "1"); //actualización del check
    }

Aquí obtendremos el valor a mostrar en el CheckBox. Dado que en este ejemplo, en la DDBB tenemos una columna ‘Bit’, deberemos marcar la selección en función de si el valor es ‘0’ ó ‘1’.



Llegados a este punto, ya tenemos configurada nuestra plantilla completa para la columna del gridView. Recordad que en este ejemplo estamos trabajando con un CheckBox, pero se podría personalizar para cualquier tipo de control e información que os hiciese falta.

En el próximo post indicaré cómo hacer uso de esta clase, para generar nuestro grid personalizado.


Referencias:
MSDN Microsoft

Un saludo a todos!

2 comentarios:

  1. como muestro una imagen en gridview pero esta incluida en un campo de mi base de datos, no es dirección de la foto si no esta en un campo image

    ResponderEliminar
    Respuestas
    1. No creo que necesites implementar todo un grid view personalizado. Simplemente podrías generar un ascx, o una página que mostrase dicha imagen a partir de un parametro dado.

      Aquí tienes una buena referencia, que espero te sirva: http://www.aspsnippets.com/articles/display-images-from-sql-server-database-in-asp.net-gridview-control.aspx

      Eliminar