Come rilevare la modifica dell'evento CheckBox di DataGridView?


90

Ho un'app winforms e desidero attivare del codice quando una casella di controllo incorporata in un DataGridViewcontrollo è selezionata / deselezionata. Ogni evento che ho provato neanche

  1. Si attiva non appena si CheckBoxfa clic su ma prima che il suo stato controllato cambi, o
  2. Si attiva solo quando CheckBoxperde la concentrazione

Non riesco a trovare un evento che si attiva immediatamente dopo la modifica dello stato controllato.


Modificare:

Quello che sto cercando di ottenere è che quando lo stato controllato di a CheckBoxin uno DataGridViewcambia, i dati in altri due DataGridViewcambiano. Eppure tutti gli eventi che ho usato, i dati nelle altre griglie cambiano solo dopo che CheckBoxla prima DataGridViewperde il focus.


2
Hai controllato CurrentCellDirtyStateChangedEvento?
Yograj Gupta

Ancora viene eseguito solo quando l'utente "lascia" la cella.
PJW

1
Ecco l'articolo MSDN su questo: msdn.microsoft.com/en-us/library/… simile ma leggermente diverso dalla risposta di Killercam
David Hall

Risposte:


96

Per gestire l' evento DatGridViews CheckedChangeddevi prima attivare il CellContentClickto fire (che non ha lo CheckBoxstato corrente es!) Quindi chiamare CommitEdit. Questo a sua volta attiverà l' CellValueChangedevento che puoi usare per fare il tuo lavoro. Questa è una svista di Microsoft . Fai qualcosa come il seguente ...

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();
}

Spero che questo possa essere d'aiuto.

PS Controlla questo articolo https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx


5
Questa è una soluzione buona, ma non funziona se l'utente fa clic più volte, in alternativa è stato postato qui sotto stackoverflow.com/questions/11843488/...
56ka

1
Suggerirei anche di NON utilizzare questa soluzione per il problema del doppio clic. La funzione EndEdit () deve essere chiamata ... trova il collegamento da @ 56ka e fai clic sul collegamento dell'articolo!
Luca

1
Non ho speso molto per questa soluzione e se quella di @ 56ka è migliore, bene. Tuttavia, non sono sicuro di quale sia il problema del doppio clic su a DataGridViewCheckBox. Questo non è WPF e fare doppio clic sul controllo non interrompe l'associazione dati, è WinForms. Il doppio clic potrebbe non aggiornare visivamente il controllo ma non interrompe nulla e in questo caso forse la soluzione di seguito è quella migliore. Grazie.
MoonKnight

Funziona perfettamente se aggiungi anche lo stesso codice da CellContentClickin CellContentDoubleClick. CellMouseUpsi attiverà anche se la cella è selezionata ma la casella di controllo non è selezionata, il che non è un comportamento desiderato.
preda torpida

89

Ho trovato la soluzione di @ Killercam per funzionare, ma era un po 'rischioso se l'utente faceva doppio clic troppo velocemente. Non sono sicuro che anche gli altri abbiano trovato il caso. Ho trovato un'altra soluzione qui .

Usa i datagrid CellValueChangede CellMouseUp. Changhong lo spiega

"Il motivo è che l'evento OnCellvalueChanged non si attiverà fino a quando DataGridView non riterrà che tu abbia completato la modifica. Questo ha senso per una colonna TextBox, poiché OnCellvalueChanged non si [preoccuperebbe] di attivarsi per ogni colpo di tasto, ma non [ ha senso] per un CheckBox. "

Eccolo in azione dal suo esempio:

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

E il codice per dire alla casella di controllo che è stato modificato quando viene cliccato, invece di aspettare che l'utente lasci il 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();
    }
}

Modifica: un evento DoubleClick viene trattato separatamente da un evento MouseUp. Se viene rilevato un evento DoubleClick, l'applicazione ignorerà completamente il primo evento MouseUp. Questa logica deve essere aggiunta all'evento CellDoubleClick oltre all'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();
    }
}

3
Mi sono imbattuto nel problema del doppio clic notato dal risponditore e questo ha funzionato molto meglio della prima soluzione nel gestirlo correttamente.
Steve Ferguson

1
Ho anche riscontrato il problema del doppio clic e questa soluzione lo ha risolto.
Chris C

Fare clic sul pulsante "qui" e consultare l'articolo. Ho avuto lo stesso problema con il doppio clic.
Luca

4
Cosa succede se si attiva l'interruttore con la barra spaziatrice?
Halfgaar

1
Per 'risolvere' il problema della barra spaziatrice, ho impostato KeyPreviewsu true nel modulo e quando e.KeyCode == Keys.Space, imposta e.Handled = true. In altre parole, ho appena disabilitato la modifica della tastiera.
Halfgaar

9

La soluzione di jsturtevants ha funzionato alla grande. Tuttavia, ho scelto di eseguire l'elaborazione nell'evento EndEdit. Preferisco questo approccio (nella mia applicazione) perché, a differenza dell'evento CellValueChanged, l'evento EndEdit non si attiva mentre si popola la griglia.

Ecco il mio codice (parte del quale è stato rubato da 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();
    }
}

3
Buona risposta, ma è preferibile utilizzare al CellContentClickposto di CellMouseUpperché quest'ultimo verrà chiamato quando l'utente fa clic in un punto qualsiasi all'interno della cella mentre il primo viene chiamato solo quando si fa clic sulla casella di controllo.
Jamie Kitson

6

Questo gestisce anche l'attivazione della tastiera.

    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.....
    }

5

Ecco un po 'di codice:

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);
    }
}

2
Questa risposta contiene la risposta corretta, che gestisce le interazioni del mouse e della tastiera e le interazioni ripetute senza lasciare la cella. Ma è necessario solo l'ultimo gestore: chiamare CommitEditda CurrentCellDirtyStateChangedè l'intera soluzione.
Ben Voigt

4

seguendo Killercam'answer, My code

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

e :

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
            }
        }
    }

2

Si tratta di modificare la cella, il problema è che la cella non è stata effettivamente modificata, quindi è necessario salvare le modifiche della cella o della riga per ottenere l'evento quando si fa clic sulla casella di controllo in modo da poter utilizzare questa funzione:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

con questo puoi usarlo anche con un evento diverso.


2

Ho trovato una risposta più semplice a questo problema. Uso semplicemente la logica inversa. Il codice è in VB ma non è molto diverso da 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 delle cose migliori di questo non è la necessità di più eventi.


1

Ciò che ha funzionato per me era CurrentCellDirtyStateChangedin combinazione 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();
}

1

Il codice verrà eseguito in loop in DataGridView e verificherà se la colonna CheckBox è selezionata

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;
        }
    }
}

1

Nell'evento CellContentClick puoi usare questa strategia:

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;
        }
    }
}

1

Ho provato alcune risposte da qui, ma ho sempre avuto qualche tipo di problema (come il doppio clic o l'utilizzo della tastiera). Quindi, ne ho combinati alcuni e ho ottenuto un comportamento coerente (non è perfetto, ma funziona correttamente).

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.

}

0

Per fare ciò quando si utilizza devexpress xtragrid, è necessario gestire l' evento EditValueChanged di un elemento del repository corrispondente come descritto qui . È anche importante chiamare il metodo gridView1.PostEditor () per assicurarsi che il valore modificato sia stato pubblicato. Ecco un'implementazione:

        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);
                    }
                }
            }
        }

Si noti che poiché xtragrid non fornisce un enumeratore, è necessario utilizzare un ciclo for per iterare sulle righe.


0

La rimozione dello stato attivo dopo le modifiche al valore della cella consente l'aggiornamento dei valori in DataGridView. Rimuovere lo stato attivo impostando CurrentCell su null.

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);
    }

}

0

Puoi forzare la cella a salvare il valore non appena fai clic sulla casella di controllo e quindi cattura l' evento CellValueChanged . Il CurrentCellDirtyStateChanged incendi non appena si fa clic sulla casella di controllo.

Il codice seguente funziona per me:

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

È quindi possibile inserire il codice nell'evento CellValueChanged .


0

Ben Voigt ha trovato la soluzione migliore in una risposta al commento sopra:

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

Seriamente, è tutto ciò di cui hai bisogno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.