sábado, 20 de marzo de 2010

C# - [DataGridView] - Parte 6 - ComboBox y evento SelectedIndexChanged

 

Introducción

Uno de los problemas al trabajar con el DataGridView y los combos en las celdas, es que no hay un eventos preciso que sea lanzado al cambiar la selección por parte del usuario.

El evento CellValueChanged se podría decir que es el mas cercano a utilizar, pero este solo se produce cuando la celda se deja de editar, o sea hay que salir de la edición de la celda, y además haber cambiado el ítem seleccionado para que el evento se produzca.

Es por este punto que este articulo explicara como adjuntar el combo definido en una columna de tipo DataGridViewComboBoxColumn, al evento SelectedIndexChanged, el cual de forma estándar no esta disponible en la grilla.

Planteo del problema

Se dispone de una grilla, la cual presenta un combo en una de sus columnas, y un check que habilita la selección de la lista de productos.

El usuario al cambiar la selección del combo, de forma automática el sistema debería marcarse el checkbox en la misma fila en edición.

Primer planteo de solución

Para resolver el problema serán necesarios dos eventos:

- EditingControlShowing, el cual se lanza cuando la celda entre en estado de edición

- SelectedIndexChanged, el cual será asignado al control combo de la celda que se este editando

 

private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
    DataGridViewComboBoxEditingControl dgvCombo = e.Control as DataGridViewComboBoxEditingControl;  
  
    if (dgvCombo != null)            
    {   
        //
        // se remueve el handler previo que pudiera tener asociado, a causa ediciones previas de la celda
        // evitando asi que se ejecuten varias veces el evento
        //
        dgvCombo.SelectedIndexChanged -= new  EventHandler(dvgCombo_SelectedIndexChanged);                
                   
        dgvCombo.SelectedIndexChanged += new EventHandler(dvgCombo_SelectedIndexChanged);            
    }

}

private void dvgCombo_SelectedIndexChanged(object sender, EventArgs e) 
{
    //
    // se recupera el valor del combo
    // a modo de ejemplo se escribe en consola el valor seleccionado
    //
    ComboBox combo = sender as ComboBox;

    Console.WriteLine(combo.SelectedValue);

    //
    // se accede a la fila actual, para trabajr con otor de sus campos
    // en este caso se marca el check si se cambia la seleccion
    //
    DataGridViewRow row = dataGridView1.CurrentRow; 

    DataGridViewCheckBoxCell cell = row.Cells["Seleccionado"] as DataGridViewCheckBoxCell;
    cell.Value = true;
}

Aquí hay algunos puntos a detallar:

- Como se observa en el evento EditingControlShowing, este tiene un argumento en el evento que permite tomar que control esta siendo editado, puntualmente el e.Control, el cual puede ser convertido a un tipo especifico se quiere trabajar, en este caso el combobox, cualquier otra celda no será del mismo tipo por lo tanto la conversión devolverá null.

Vale aclarar que en este caso usar esta línea:

DataGridViewComboBoxEditingControl dgvCombo = e.Control as DataGridViewComboBoxEditingControl; 

o esta otra:

ComboBox dgvCombo = e.Control as ComboBox;

es indiferente, con ambas funciona correctamente.

- Seguramente se preguntaran porque se esta realizando la desasignación del evento, cuando en la línea siguiente se vuelve adjunta. Esto básicamente se realiza porque si en varias oportunidades es editada la misma celda, en cada ingreso al evento se asignaría un nuevo handler, o sea no es pisado el previo o existente, provocando que se lance mas de una vez el mismo evento, lo cual no es el efecto deseado.

- En este ejemplo no se hizo, pero podría haberse preguntado si el control en edición es del tipo ComboBox, mediante el agregado de if, y el uso del is, para luego en caso de ser afirmativo en ese caso si convertir al tipo necesario.

private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
 {
     if (e.Control is ComboBox)            
     {
         DataGridViewComboBoxEditingControl dgvCombo = e.Control as DataGridViewComboBoxEditingControl;  
         
         //
         // se remueve el handler previo que pudiera tener asociado, a causa ediciones previas de la celda
         // evitando asi que se ejecuten varias veces el evento
         //
         dgvCombo.SelectedIndexChanged -= new  EventHandler(dvgCombo_SelectedIndexChanged);                
                    
         dgvCombo.SelectedIndexChanged += new EventHandler(dvgCombo_SelectedIndexChanged);            
     }

 }

 

[C#]
[VB.NET]

 

Problema detectado en la primer solución

Si bien el ejemplo anterior funciona correctamente a primera vista, hay un efecto que se puede llegar a manifestarse, el cual no es nada deseable.

Resulta que en ciertas ocasiones luego de haber editado una de las celdas del combo y seleccionado un ítem, esta funciono correctamente y marco el check de la fila.

Pero al editar otra celda en una fila distinta, sin haber cambiado opción alguna, se dispara el evento del combo, marcando el check, cuando no debería hacerlo en ese momento, ya que no hubo cambio de selección alguna.

Esto se debe a que el combo queda con el evento asignado, y lo lanza cuando entra en edición.

Segundo Planteo de solución

Este escenario, si bien resuelve el efecto en la selección descripto en los pasos previo, tiene un punto no tan bonito en el código, ya que debe conservar el control que se esta editando de forma global al formulario.

Básicamente la resolución del problema es realizada mediante la quita del evento del combo cuando se deja de editar la celda, para lo cual se agrega el evento CellEndEdit.

 

DataGridViewComboBoxEditingControl dgvCombo;

private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
    dgvCombo = e.Control as DataGridViewComboBoxEditingControl;  
  
    if (dgvCombo != null)            
    {   
        dgvCombo.SelectedIndexChanged += new EventHandler(dvgCombo_SelectedIndexChanged);            
    }

}

private void dvgCombo_SelectedIndexChanged(object sender, EventArgs e) 
{
    //
    // se recupera el valor del combo
    // a modo de ejemplo se escribe en consola el valor seleccionado
    //
    ComboBox combo = sender as ComboBox;

    Console.WriteLine(combo.SelectedValue);

    //
    // se accede a la fila actual, para trabajr con otor de sus campos
    // en este caso se marca el check si se cambia la seleccion
    //
    DataGridViewRow row = dataGridView1.CurrentRow; 

    DataGridViewCheckBoxCell cell = row.Cells["Seleccionado"] as DataGridViewCheckBoxCell;
    cell.Value = true;
}

private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{

    if (dgvCombo != null)
        dgvCombo.SelectedIndexChanged -= new EventHandler(dvgCombo_SelectedIndexChanged);                

}

Puntos a remarcar:

- Se debe conservar el control combobox editado de forma global del formulario, de esta forma al terminar la edición, poder remover el evento.

Este problema se presenta ya que no existe algún otro evento en la grilla, en donde su argumento devuelva el control que lo genera, de la misma forma en que lo hace el evento EditingControlShowing, con su argumento e.Control

- En el evento EditingControlShowing solo hace falta agregar el handler al evento, ya que la remoción se realiza en esta oportunidad cuando es terminada la edición en el evento CellEndEdit

 

[C#]
[VB.NET]

22 comentarios:

Luys dijo...

Hola Buen dia.

Ya segui el tutorial de este post y me funciona a madias. Si no me equivoco lo que me hace falta es cargar el evento CellEndEdit, y honestamente no tengo idea de como mandarlo llamar. Ojala puedas ayudarme. Te dejo mi correo luys.carreon@hotmail.com y sistemas.programacion@brantano.com.mx

Leandro Tuttini dijo...

hola Luys

el CellEndEdit es un evento, solo lo defines en el datagridview

seleccionas el grid, presionas F4 para ver sus propiedades y en el solution explorer veras un icono con forma de rayo amarillo, si lo presionas veras los eventos del datagridview, alli tendrias que tener el CellEndEdit

saludos

Jorge dijo...

From Luys:

Hola Leandro.

Muy buen post, me ha sido de gran ayuda. Tuve el mismo problema que Luys y tambien ya quedó resuelto.

Pero tengo otra consulta. Te planteo el problema: Tengo un formulario (aplicacion de Windows) con un DataGridView, este a su ves tiene multiples columnas, algunas son DataGridViewTextBoxColumn y otras DataGridViewComboBoxColumn. Mi objetivo es llenar el DataGridView con datos q tomo de una BD. El problema surge cuando tengo q asignar la informacion recuperada de la consulta al DataGridView especificamente a las columnas DataGridViewComboBox.

Como punto extra: La coleccion de cada una de las columnas las cargo con DataTable's en consultas previas, en todo caso lo unico que necesito hacer es indicarle a la aplicacion cuál es el valor que quiero que seleccione en cada DataGridViewComboBoxCell.

De antemano muchas gracias!

Shark87 dijo...

Muchisimas Gracias me funciono el 2do ejemplo a la perfección! =D

Jonathan Muñoz Solano dijo...

Hola Leaondro, tengo un problema, tengo una grilla que esta en lazada a una base de datos, y con una columna de checkBox, el problema que me surge es que no puedo marcar los checkbox, a q se debe y como podria solucionarlo?

Leandro Tuttini dijo...

hola Jonathan

has verificado que el grid no tenga la propeidad ReadOnly en true

si el control esta como solo lectura no podras modificar las celdas

saludos

Don Gabo dijo...

Estimado junto con agradecer tu ayuda, estoy topando en algo, tengo un gridview con un combobox en la ultima columna, con la alternativa S o N (si o no), cuando seleciono una opcion,sino pierde el foco el combobox el valor no pasa a la celda no actualizando el valor de esta, y la verdad no se como controlar esto, agradecería alguna ayuda.

Leandro Tuttini dijo...

hola Don Gabo

no entendi, como es eso que no se pasa el valor a la celda, si el combo esta dentro de la misma celda del gridview

estas definiendo el grid en modo edicion para que muestre el combo y al aceptar vuelve a mostrar un label ?

como aqui

[ASP.NET][GridView] Edición usando DropDownList

saludos

Don Gabo dijo...

Leandro, gracias por responder, el tema es como automatizar cuando dices "al aceptar", ese enter,recien lo hice mediante un sendkeys y me funciono, habra otra alternartiva, use tu ejemplo.

Leandro Tuttini dijo...

hola Don Gabo

la verdad sigo sin entender
por lo que veo usa un datagridview, y no un gridview (el cual es un control de asp.net)

no logro comprender que es eso del "al aceptar" que comentas, porque seleccionar un item del combo no lanza ningun mensaje que se deba aceptar

saludos

GuiPulk dijo...

Que tal... por favor necesito ayuda, en ejecucion tengo mi datagridview con una celda que posee un combo box de dos items, pero al intentar seleccionar se me detiene la ejecucion... Gracias por su ayuda...

Leandro Tuttini dijo...

hola GuiPulk

creo que deberias aportar algo mas de info sobre el problema, el "detenerse" la aplicacion la verdad no me indica mucho

o sea cuando se detiene es por algun error? pudes poner algun try..catch en el codigo para poder atrapar el problema y ver cual es el mensaje

saludos

Federico Marquez R dijo...

Saludos Leandro,

He estado siguiendo todas las indicaciones que me han hecho por este foro y he logrado avanzar poco a poco en mi proyecto. Hace algun momento me encontre con tu blog y me parecio bastante completo, en especial el articulo siguiente:

[DataGridView] - Parte 6 - ComboBox y evento SelectedIndexChanged de fecha sábado, 20 de marzo de 2010.

He estado leyendo atentativamente tu articulo y algo parecido es lo que necesito hacer, en otras palabras necesito crear una GridVIew (o algo por estilo) donde pueda insertar uno (o varios) comboBox que me permitan escoger entre diferentes instrumentos. La DataGrid o Gridview (o lo que tu me recomiendes) esta compuesta por varias columnas. EN la primera columna tendria los combobox donde seleccionaria los instrumentos y algunos parametros. En las columnas siguientes se visualizarian (solo texto) los parametros e instrumentos escogidos en la combobox.

Desde tu blog no pude descargar el programa en VB.NET. Podrias decirme como descargarlo ya que es en este lenguaje en el que estoy programando mi aplicacion. Como hago para suscribirme a tu blog.

Te agradezco de antemano tu ayuda.

Yo estoy realizando un proyecto final de carrera y es por ello que necesito avanzar en esto.

Gracias.

Leandro Tuttini dijo...

hola Federico

que tipo de desarrollo estas realizando?, porque el gridview es un control de asp.net, pero el articulo trata el grid de winforms

saludos

Federico Marquez R dijo...

Eso es correcto, es un proyecto Windows Application.



Gracias

Leandro Tuttini dijo...

hola Federico

quizas buscas algo como esto

[DataGridView] – Parte 4 - Uso del DataGridViewComboBoxColumn

analiza la seccion: "3 – Realizar una operación al cambiar la selección del combo"

por lo que planteas es lo que necesitas

saludos

Julio César Hernández Ortega dijo...

Hola, que tal...seguí el tutorial y todo va bien...tengo duda de como puedo obtener el id del elemento que selecciono del combobox ya que estoy guardando todo lo que pongo en la grilla a una base de datos y no logro obtener el id de cada combobox para guardarlo..saludos

Leandro Tuttini dijo...

hola Julio

pero el id del item del combo en la celda deberia venir el la propiedad Value

o sea cuando tomas el Value de la celda se recupera el id, eso si vas a grabar accediendo a la celdas

si la idea es tomar el valor desde el evento del combo deberias usar el SelectedValue, pero recuerda definir el ValueMember para poder usar esa propiedad

saludos

Federico Marquez R dijo...

Que tal Leandro,
Tengo un inconveniente con una serializacion que estoy realizando. Quisiera saber si podrias darme algun consejo al respecto. El contenido de mi pregunta se encuentra en el sitio:
http://social.msdn.microsoft.com/Forums/es-ES/3a074b38-ca7f-4398-b47f-7c2128ba4d04/problema-con-serializacion-xml-aplicacion-wpf

Disculpa que te realize esta pregunta por tu blog, pero no encontre manera de enviarte un mensaje directamente desde MSDN.
Espero alguna sugerencia.
Muchas gracias.

Leandro Tuttini dijo...

hola Federico

respondi en el foro

saludos

Nelson Jimmy Cusi CCama dijo...

hola leandro vi tus ejercicios y revice tu ejemplo que por cierto es muy interesante ver los efectos que pueden tener los eventos, quiero consultarte sobre un tema similar.

tengo un datagridview el cual tiene el evento CellFormating este me permite formatear ciertas formulas que yo ingreso y mostrar su resultado me funciona bien. ya que al hacer click me muestrar la formula aritmetica y al salir de la celda queda el resultado de esa formula, pero le acabo de cambiar una columna por un DataGridViewComboBox, este esta enlazado con la Base de Datos. en el DisplayMember Cargo los nombres que quiero mostrar , y en ValueMember el codigo, El problema es que al selecionar un Item(nombre) del DataGridViewComboBox establese el codigo mas no el nombre que quiero mostrar me espero me pueda ayudar.. porfavor..

Leandro Tuttini dijo...

hola Nelson

no entendi al relacion entre el CellFormatting y la columna de tipo combo, porque hasta donde imagino esta columna deberias evitarla en el Cellformatting

la columna combo deberia tener un campo que defina el DatapropertyName y asi poder determinar que valor seleccionar cuando asignes el Datasource

por supuesto la propiedad que definas en el DatapropertyName deberia coincidir con los datoa que usaste para cargar los items del combo

a donde voy es que el Cellformatting en este caso no aplicaria, deberias validar que la columna es la del combo y saltearla

saludos