web-dev-qa-db-fra.com

Comment détecter un changement d'événement DataGridView CheckBox?

J'ai une application Winforms et je veux déclencher du code lorsqu'une case à cocher intégrée à un contrôle DataGridView est cochée/décochée. Chaque événement que j'ai essayé soit

  1. Se déclenche dès que vous cliquez sur CheckBox mais avant que son état vérifié change, ou
  2. Déclenche une fois que la variable CheckBox perd son focus

Je n'arrive pas à trouver un événement qui se déclenche immédiatement après le changement d'état coché.


Modifier:

Ce que j'essaie de faire, c'est que lorsque l'état vérifié d'une CheckBox dans une DataGridView change, les données de deux autres DataGridViews changent. Pourtant, pour tous les événements que j'ai utilisés, les données des autres grilles ne sont modifiées qu'après la CheckBox de la première DataGridView perd le focus. 

63
PJW

Pour gérer l'événement DatGridViews CheckedChanged, vous devez d'abord déclencher la CellContentClick (qui n'a pas l'état actuel CheckBoxes!), Puis appeler CommitEdit. Cela déclenchera à son tour l'événement CellValueChanged que vous pourrez utiliser pour effectuer votre travail. Ceci est un oubli de Microsoft. Faites quelque chose comme ce qui suit ...

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

J'espère que ça aide.

P.S. Consultez cet article https://msdn.Microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx

76
MoonKnight

La solution de @ Killercam fonctionnait bien, mais j'étais un peu louche si l'utilisateur double-cliquait trop rapidement. Je ne sais pas si les autres ont trouvé que le cas non plus. J'ai trouvé une autre solution ici .

Il utilise les variables CellValueChanged et CellMouseUp de la grille de données. Changhong explique que 

"La raison en est que l'événement OnCellvalueChanged ne se déclenchera pas tant que le DataGridView n'aura pas terminé l'édition. Cela donne un sens à une colonne TextBox, car OnCellvalueChanged ne se déclenche pas à chaque frappe, mais ce n'est pas le cas [ logique] pour un CheckBox ".

Ici, il est en action de son exemple:

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

Et le code pour indiquer à la case à cocher que l'édition est terminée lorsque l'utilisateur clique dessus, au lieu d'attendre que l'utilisateur quitte le champ:

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

la solution de jsturtevants a très bien fonctionné. Cependant, j'ai choisi d'effectuer le traitement dans l'événement EndEdit. Je préfère cette approche (dans mon application) car, contrairement à l'événement CellValueChanged, l'événement EndEdit ne se déclenche pas lorsque vous remplissez la grille.

Voici mon code (dont une partie est volée à 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();
    }
}
7
Mark Ainsworth

Cela gère également l'activation du clavier.

    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
Chuck Fecteau

suite à Killercam'answer, mon code 

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

et :

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
            }
        }
    }
4
Nghĩa Lê

Voici du code:

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);
    }
}
4
Nay Lin Aung

J'ai trouvé une réponse plus simple à ce problème. J'utilise simplement la logique inverse. Le code est dans VB mais il n’est pas très différent de 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

L'une des meilleures choses à ce sujet n'est pas la nécessité de plusieurs événements.

2
Jimva

Il s’agit de modifier la cellule. Le problème, c’est que la cellule n’a pas été modifiée, vous devez donc enregistrer les modifications de la cellule ou de la ligne pour obtenir l’événement lorsque vous cochez la case pour pouvoir utiliser cette fonction:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

avec cela, vous pouvez l'utiliser même avec un événement différent.

2
ahmedcool166

Dans le cas où CellContentClick, vous pouvez utiliser cette stratégie:

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
daniele3004

Le code bouclera dans DataGridView et vérifiera si la colonne CheckBox est cochée

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
E Coder

Ce qui a fonctionné pour moi était CurrentCellDirtyStateChanged en combinaison avec datagridView1.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
beetroot123

Supprimer le focus après les changements de valeur de cellule permet aux valeurs de se mettre à jour dans DataGridView. Supprimez le focus en définissant le CurrentCell sur 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
Branden Huggins

Pour ce faire, lorsque vous utilisez xtragrid devexpress, il est nécessaire de gérer l'événement EditValueChanged d'un élément de référentiel correspondant, comme décrit ici . Il est également important d'appeler la méthode gridView1.PostEditor () pour vérifier que la valeur modifiée a été publiée. Voici une implémentation:

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

Notez que, comme xtragrid ne fournit pas d’énumérateur, il est nécessaire d’utiliser une boucle for pour effectuer une itération sur les lignes.

0
majjam

Vous pouvez forcer la cellule à valider la valeur dès que vous cliquez sur la case à cocher, puis attrapez l'événement CellValueChanged . Le CurrentCellDirtyStateChanged se déclenche dès que vous cochez la case. 

Le code suivant fonctionne pour moi:

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

Vous pouvez ensuite insérer votre code dans l'événement CellValueChanged .

0
David Ruiz

J'ai trouvé une réponse plus simple à ce problème. J'utilise simplement la logique inverse. Le code est dans VB mais il n’est pas très différent de C #.

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

    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

L'une des meilleures choses à ce sujet n'est pas la nécessité de plusieurs événements.

0
Jimva

J'ai essayé quelques réponses à partir de là, mais j'ai toujours eu une sorte de problème (comme un double clic ou en utilisant le clavier). J'ai donc combiné certains d'entre eux et obtenu un comportement cohérent (ce n'est pas parfait, mais ça fonctionne correctement).

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
Félix Severo