¿Cómo detectar el cambio de evento DataGridView CheckBox?

90

Tengo una aplicación winforms y quiero activar algún código cuando una casilla de verificación incrustada en un DataGridViewcontrol está marcada / desmarcada. Cada evento que he probado

  1. Se activa tan pronto como CheckBoxse hace clic en pero antes de que cambie su estado marcado, o
  2. Se dispara solo una vez que CheckBoxpierde su enfoque

Parece que no puedo encontrar un evento que se active inmediatamente después de que cambie el estado verificado.


Editar:

Lo que estoy tratando de lograr es que cuando el estado verificado de a CheckBoxen uno DataGridViewcambia, los datos en otros dos DataGridViewcambian. Sin embargo, todos los eventos que he usado, los datos en las otras cuadrículas solo cambian después de que CheckBoxen la primera DataGridViewpierde el foco.

PJW
fuente
2
¿Marcó CurrentCellDirtyStateChangedEvento?
Yograj Gupta
Todavía solo se ejecuta cuando el usuario "abandona" la celda.
PJW
1
Aquí está el artículo de MSDN sobre esto: msdn.microsoft.com/en-us/library/ ... similar pero un poco diferente a la respuesta de Killercam
David Hall

Respuestas:

96

Para manejar el evento DatGridViews CheckedChanged, primero debe hacer CellContentClickque se dispare (¡que no tiene el CheckBoxestado actual es!) Y luego llamar CommitEdit. Esto, a su vez, activará el CellValueChangedevento que puede utilizar para hacer su trabajo. Este es un descuido de Microsoft . Haz algo como lo siguiente ...

private void dataGridViewSites_CellContentClick(object sender, 
    DataGridViewCellEventArgs e)
{
    dataGridViewSites.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

/// <summary>
/// Works with the above.
/// </summary>
private void dataGridViewSites_CellValueChanged(object sender, 
    DataGridViewCellEventArgs e)
{
    UpdateDataGridViewSite();
}

Espero que esto ayude.

PS Consulte este artículo https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx

Caballero de la Luna
fuente
5
Esta es una buena solución pero no funciona si el usuario hace clic varias veces, se ha publicado una alternativa debajo de stackoverflow.com/questions/11843488/…
56ka
1
También recomiendo encarecidamente NO utilizar esta solución para el problema del doble clic. Es necesario llamar a la función EndEdit () ... busque el enlace de @ 56ka y haga clic en el enlace del artículo.
Lucas
1
No pasé mucho tiempo en esta solución y si la solución de @ 56ka es mejor, genial. Sin embargo, no estoy seguro de qué tanto alboroto por hacer doble clic en a DataGridViewCheckBox. Esto no es WPF y hacer doble clic en el control no rompe ningún enlace de datos, es WinForms. Es posible que hacer doble clic no actualice el control visualmente, pero no rompe nada y, en este caso, quizás la solución a continuación sea la mejor. Gracias.
MoonKnight
Esto funciona perfectamente si agrega el mismo código desde CellContentClickdentro CellContentDoubleClick. CellMouseUpSe activará incluso si la celda está seleccionada pero no se hace clic en la casilla de verificación, lo que no es el comportamiento deseado.
presa
89

Encontré que la solución de @Killercam funcionaba, pero era un poco dudosa si el usuario hacía doble clic demasiado rápido. No estoy seguro si otros también encontraron ese caso. Encontré otra solución aquí .

Utiliza las redes de datos CellValueChangedy CellMouseUp. Changhong explica que

"La razón de esto es que el evento OnCellvalueChanged no se activará hasta que DataGridView crea que ha completado la edición. Esto tiene sentido para una columna TextBox, ya que OnCellvalueChanged no [se molestaría] en activarse con cada pulsación de tecla, pero no [ tiene sentido] para un CheckBox ".

Aquí está en acción a partir de su ejemplo:

private void myDataGrid_OnCellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
    {
        // Handle checkbox state change here
    }
}

Y el código para indicarle a la casilla de verificación que ha terminado de editar cuando se hace clic en él, en lugar de esperar hasta que el usuario abandona el campo:

private void myDataGrid_OnCellMouseUp(object sender,DataGridViewCellMouseEventArgs e)
{
    // End of edition on each click on column of checkbox
    if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
    {
        myDataGrid.EndEdit();
    }
}

Editar: un evento de DoubleClick se trata por separado de un evento MouseUp. Si se detecta un evento de DoubleClick, la aplicación ignorará el primer evento MouseUp por completo. Esta lógica debe agregarse al evento CellDoubleClick además del evento MouseUp:

private void myDataGrid_OnCellDoubleClick(object sender,DataGridViewCellEventArgs e)
{
    // End of edition on each click on column of checkbox
    if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
    {
        myDataGrid.EndEdit();
    }
}
jsturtevant
fuente
3
Me encontré con el problema del doble clic observado por el respondedor, y este funcionó mucho mejor que la primera solución para manejarlo correctamente.
Steve Ferguson
1
También encontré el problema del doble clic y esta solución lo solucionó.
Chris C
Haga clic en el botón "aquí" y consulte el artículo. Tuve el mismo problema con el doble clic.
Lucas
4
¿Qué pasa si alternas la palanca con la barra espaciadora?
Halfgaar
1
Para 'arreglar' el problema de la barra espaciadora, configuro KeyPreviewcomo verdadero en el formulario y cuando e.KeyCode == Keys.Space, configuro e.Handled = true. En otras palabras, acabo de deshabilitar la edición del teclado.
Halfgaar
9

La solución de jsturtevants funcionó muy bien. Sin embargo, opté por hacer el procesamiento en el evento EndEdit. Prefiero este enfoque (en mi aplicación) porque, a diferencia del evento CellValueChanged, el evento EndEdit no se activa mientras se completa la cuadrícula.

Aquí está mi código (parte del cual es robado de jsturtevant:

private void gridCategories_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex == gridCategories.Columns["AddCategory"].Index)
    {
        //do some stuff
    }
}



private void gridCategories_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.ColumnIndex == gridCategories.Columns["AddCategory"].Index)
    {
        gridCategories.EndEdit();
    }
}
Mark Ainsworth
fuente
3
Buena respuesta, pero es preferible usarla en CellContentClicklugar de CellMouseUpporque se llamará a la última cuando el usuario haga clic en cualquier lugar dentro de la celda, mientras que a la primera solo se le llamará cuando se haga clic en la casilla de verificación.
Jamie Kitson
6

Esto también maneja la activación del teclado.

    private void dgvApps_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
        if(dgvApps.CurrentCell.GetType() == typeof(DataGridViewCheckBoxCell))
        {
            if (dgvApps.CurrentCell.IsInEditMode)
            {
                if (dgvApps.IsCurrentCellDirty)
                {
                    dgvApps.EndEdit();
                }
            }
        }
    }


    private void dgvApps_CellValueChanged(object sender, DataGridViewCellEventArgs e)
    {
          // handle value changed.....
    }
Chuck Fecteau
fuente
5

Aquí hay un código:

private void dgvStandingOrder_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    if (dgvStandingOrder.Columns[e.ColumnIndex].Name == "IsSelected" && dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
    {
        bool isChecked = (bool)dgvStandingOrder[e.ColumnIndex, e.RowIndex].EditedFormattedValue;
        if (isChecked == false)
        {
            dgvStandingOrder.Rows[e.RowIndex].Cells["Status"].Value = "";
        }
        dgvStandingOrder.EndEdit();
    }
}

private void dgvStandingOrder_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{

    dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

private void dgvStandingOrder_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
    {
        dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }
}
Nay Lin Aung
fuente
2
Esta respuesta contiene la respuesta correcta, que maneja las interacciones del mouse y el teclado, y las interacciones repetidas sin salir de la celda. Pero solo se necesita el último controlador: llamar CommitEditdesde CurrentCellDirtyStateChangedes la solución completa.
Ben Voigt
4

siguiendo Killercam'answer, Mi código

private void dgvProducts_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
        dgvProducts.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }

y:

private void dgvProducts_CellValueChanged(object sender, DataGridViewCellEventArgs e)
    {
        if (dgvProducts.DataSource != null)
        {
            if (dgvProducts.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString() == "True")
            {
                //do something
            }
            else
            {
               //do something
            }
        }
    }
Nghĩa Lê
fuente
2

Se trata de editar la celda, el problema es que la celda no se editó en realidad, por lo que debe guardar los cambios de la celda o la fila para obtener el evento cuando hace clic en la casilla de verificación para poder usar esta función:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

con esto puedes usarlo incluso con un evento diferente.

ahmedcool166
fuente
2

He encontrado una respuesta más sencilla a este problema. Simplemente uso lógica inversa. El código está en VB pero no es muy diferente a C #.

 Private Sub DataGridView1_CellContentClick(sender As Object, e As 
 DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick

    Dim _ColumnIndex As Integer = e.ColumnIndex
    Dim _RowIndex As Integer = e.RowIndex

    'Uses reverse logic for current cell because checkbox checked occures 
     'after click
    'If you know current state is False then logic dictates that a click 
     'event will set it true
    'With these 2 check boxes only one can be true while both can be off

    If DataGridView1.Rows(_RowIndex).Cells("Column2").Value = False And 
       DataGridView1.Rows(_RowIndex).Cells("Column3").Value = True Then
        DataGridView1.Rows(_RowIndex).Cells("Column3").Value = False
    End If

    If DataGridView1.Rows(_RowIndex).Cells("Column3").Value = False And 
    DataGridView1.Rows(_RowIndex).Cells("Column2").Value = True Then
        DataGridView1.Rows(_RowIndex).Cells("Column2").Value = False
    End If


End Sub

Una de las mejores cosas de esto es que no es necesario realizar varios eventos.

Jimva
fuente
1

Lo que funcionó para mí fue CurrentCellDirtyStateChangeden combinación condatagridView1.EndEdit()

private void dataGridView1_CurrentCellDirtyStateChanged( object sender, EventArgs e ) {
    if ( dataGridView1.CurrentCell is DataGridViewCheckBoxCell ) {
        DataGridViewCheckBoxCell cb = (DataGridViewCheckBoxCell)dataGridView1.CurrentCell;
        if ( (byte)cb.Value == 1 ) {
            dataGridView1.CurrentRow.Cells["time_loadedCol"].Value = DateTime.Now.ToString();
        }
    }
    dataGridView1.EndEdit();
}
remolacha123
fuente
1

El código se repetirá en DataGridView y comprobará si la columna CheckBox está marcada

private void dgv1_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.ColumnIndex == 0 && e.RowIndex > -1)
    {
        dgv1.CommitEdit(DataGridViewDataErrorContexts.Commit);
        var i = 0;
        foreach (DataGridViewRow row in dgv1.Rows)
        {
            if (Convert.ToBoolean(row.Cells[0].Value))
            {
                i++;
            }
        }

        //Enable Button1 if Checkbox is Checked
        if (i > 0)
        {
            Button1.Enabled = true;
        }
        else
        {
            Button1.Enabled = false;
        }
    }
}
Codificador E
fuente
1

En el caso de CellContentClick, puede utilizar esta estrategia:

private void myDataGrid_CellContentClick(object sender, DataGridViewCellEventArgs e)
{    
    if (e.ColumnIndex == 2)//set your checkbox column index instead of 2
    {   //When you check
        if (Convert.ToBoolean(myDataGrid.Rows[e.RowIndex].Cells[2].EditedFormattedValue) == true)
        {
            //EXAMPLE OF OTHER CODE
            myDataGrid.Rows[e.RowIndex].Cells[5].Value = DateTime.Now.ToShortDateString();

            //SET BY CODE THE CHECK BOX
            myDataGrid.Rows[e.RowIndex].Cells[2].Value = 1;
        }
        else //When you decheck
        {
            myDataGrid.Rows[e.RowIndex].Cells[5].Value = String.Empty;

            //SET BY CODE THE CHECK BOX
            myDataGrid.Rows[e.RowIndex].Cells[2].Value = 0;
        }
    }
}
daniele3004
fuente
1

Probé algunas respuestas desde aquí, pero siempre he tenido algún tipo de problema (como hacer doble clic o usar el teclado). Entonces, combiné algunos de ellos y obtuve un comportamiento consistente (no es perfecto, pero funciona correctamente).

void gridView_CellContentClick(object sender, DataGridViewCellEventArgs e) {
  if(gridView.CurrentCell.GetType() != typeof(DataGridViewCheckBoxCell))
    return;
  if(!gridView.CurrentCell.IsInEditMode)
    return;
  if(!gridView.IsCurrentCellDirty)
    return;
  gridView.EndEdit();
}

void gridView_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e) {
  if(e.ColumnIndex == gridView.Columns["cFlag"].Index && e.RowIndex >= 0)
    gridView.EndEdit();
}

void gridView_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
  if(e.ColumnIndex != gridView.Columns["cFlag"].Index || e.RowIndex < 0)
    return;

  // Do your stuff here.

}
Félix Severo
fuente
0

Para hacer esto cuando se usa devexpress xtragrid, es necesario manejar el evento EditValueChanged de un elemento del repositorio correspondiente como se describe aquí . También es importante llamar al método gridView1.PostEditor () para asegurarse de que se haya publicado el valor modificado. Aquí hay una implementación:

        private void RepositoryItemCheckEdit1_EditValueChanged(object sender, System.EventArgs e)
        {
            gridView3.PostEditor();

            var isNoneOfTheAboveChecked = false;

            for (int i = 0; i < gridView3.DataRowCount; i++)
            {
                if ((bool) (gridView3.GetRowCellValue(i, "NoneOfTheAbove")) && (bool) (gridView3.GetRowCellValue(i, "Answer")))
                {
                    isNoneOfTheAboveChecked = true;
                    break;
                }
            }

            if (isNoneOfTheAboveChecked)
            {
                for (int i = 0; i < gridView3.DataRowCount; i++)
                {
                    if (!((bool)(gridView3.GetRowCellValue(i, "NoneOfTheAbove"))))
                    {
                        gridView3.SetRowCellValue(i, "Answer", false);
                    }
                }
            }
        }

Tenga en cuenta que debido a que xtragrid no proporciona un enumerador, es necesario utilizar un bucle for para iterar sobre las filas.

majjam
fuente
0

Quitar el foco después de los cambios de valor de celda permite que los valores se actualicen en DataGridView. Elimine el foco estableciendo CurrentCell en nulo.

private void DataGridView1OnCellValueChanged(object sender, DataGridViewCellEventArgs dataGridViewCellEventArgs)
{
    // Remove focus
    dataGridView1.CurrentCell = null;
    // Put in updates
    Update();
}

private void DataGridView1OnCurrentCellDirtyStateChanged(object sender, EventArgs eventArgs)
{
    if (dataGridView1.IsCurrentCellDirty)
    {
        dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }

}
Branden Huggins
fuente
0

Puede forzar a la celda a confirmar el valor tan pronto como haga clic en la casilla de verificación y luego capture el evento CellValueChanged . El CurrentCellDirtyStateChanged incendios tan pronto como haga clic en la casilla de verificación.

El siguiente código funciona para mí:

private void grid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        SendKeys.Send("{tab}");
    }

Luego puede insertar su código en el evento CellValueChanged .

David Ruiz
fuente
0

Ben Voigt encontró la mejor solución en un comentario-respuesta anterior:

private void dgvStandingOrder_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
        dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

En serio, eso es TODO lo que necesitas.

Roger M. Wilcox
fuente