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

90

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

  1. Se déclenche dès que l'utilisateur CheckBoxclique sur mais avant que son état vérifié ne change, ou
  2. Ne se déclenche qu'une fois que le CheckBoxfocus perd sa concentration

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


Éditer:

Ce que j'essaie de réaliser, c'est que lorsque l'état vérifié d'un CheckBoxdans un DataGridViewchange, les données de deux autres DataGridViewchangent. Pourtant, tous les événements que j'ai utilisés, les données des autres grilles ne changent qu'après que CheckBoxle premier DataGridViewperd son focus.

PJW
la source
2
Avez-vous vérifié l' CurrentCellDirtyStateChangedévénement?
Yograj Gupta
Ne s'exécute toujours que lorsque l'utilisateur «quitte» la cellule.
PJW
1
Voici l'article MSDN à ce sujet: msdn.microsoft.com/en-us/library / ... similaire mais un peu différent de la réponse de Killercam
David Hall

Réponses:

96

Pour gérer l' événement DatGridViews CheckedChanged, vous devez d'abord obtenir le CellContentClickto fire (qui n'a pas l' CheckBoxétat actuel de es!) Puis appeler CommitEdit. Cela déclenchera à son tour l' CellValueChangedévénement que vous pourrez utiliser pour faire votre travail. Il s'agit d'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.

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

MoonKnight
la source
5
C'est une bonne solution mais ne fonctionne pas si l'utilisateur clique plusieurs fois, une alternative a été postée ci-dessous stackoverflow.com/questions/11843488
...
1
Je suggère également fortement de NE PAS utiliser cette solution pour le problème du double clic. La fonction EndEdit () doit être appelée ... trouvez le lien de @ 56ka et cliquez sur le lien de l'article!
Luke
1
Je n'ai pas passé longtemps sur cette solution et si la solution de @ 56ka est meilleure, tant mieux. Cependant, je ne suis pas sûr de savoir ce que signifie le fait de double-cliquer sur a DataGridViewCheckBox. Ce n'est pas WPF et double-cliquer sur le contrôle ne rompt aucune liaison de données, c'est WinForms. Un double-clic peut ne pas mettre à jour le contrôle visuellement, mais il ne casse rien et dans ce cas, la solution ci-dessous est peut-être la meilleure. Merci.
MoonKnight
Cela fonctionne parfaitement si vous ajoutez également le même code depuis CellContentClickdans CellContentDoubleClick. CellMouseUpCela se déclenchera même si la cellule est sélectionnée mais que la case n'est pas cochée - ce qui n'est pas le comportement souhaité.
proie torpide
89

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

Il utilise les datagrid CellValueChangedet CellMouseUp. Changhong explique que

"La raison en est que l'événement OnCellvalueChanged ne se déclenchera pas tant que DataGridView n'aura pas pensé que vous avez terminé l'édition. Cela rend sens pour une colonne TextBox, car OnCellvalueChanged ne [dérangerait] pas de se déclencher pour chaque frappe de touche, mais il ne le fait pas [ fait sens] pour un CheckBox. "

Le voici en action d'après 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 qu'il est terminé de modifier quand il est cliqué, 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();
    }
}

Modifier: un événement DoubleClick est traité séparément d'un événement MouseUp. Si un événement DoubleClick est détecté, l'application ignorera entièrement le premier événement MouseUp. Cette logique doit être ajoutée à l'événement CellDoubleClick en plus de l'événement 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
la source
3
J'ai rencontré le problème de double-clic noté par le répondeur, et celui-ci fonctionnait beaucoup mieux que la première solution pour gérer cela correctement.
Steve Ferguson
1
J'ai également rencontré le problème du double-clic et cette solution l'a résolu.
Chris C
Cliquez sur le bouton «ici» et consultez l'article. J'ai eu le même problème avec le double clic.
Luke
4
Que faire si vous basculez la bascule avec la barre d'espace?
Halfgaar
1
Pour «résoudre» le problème de la barre d'espace, j'ai défini KeyPreviewsur true sur le formulaire et quand e.KeyCode == Keys.Space, défini e.Handled = true. En d'autres termes, je viens de désactiver l'édition du clavier.
Halfgaar
9

La solution de jsturtevants a très bien fonctionné. Cependant, j'ai choisi de faire 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();
    }
}
Mark Ainsworth
la source
3
Bonne réponse, mais il est préférable d'utiliser CellContentClickau lieu de CellMouseUpcar ce dernier sera appelé lorsque l'utilisateur cliquera n'importe où dans la cellule alors que le premier n'est appelé que lorsque la case est cochée.
Jamie Kitson
6

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.....
    }
Chuck Fecteau
la source
5

Voici un 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);
    }
}
Nay Lin Aung
la source
2
Cette réponse contient la bonne réponse, qui gère à la fois les interactions avec la souris et le clavier et les interactions répétées sans quitter la cellule. Mais seul le dernier gestionnaire est nécessaire - appeler CommitEditdepuis CurrentCellDirtyStateChangedest la solution complète.
Ben Voigt
4

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
            }
        }
    }
Nghĩa Lê
la source
2

Il s'agit de modifier la cellule, le problème est que la cellule n'a pas été modifiée en fait, vous devez donc enregistrer les modifications de la cellule ou de la ligne pour obtenir l'événement lorsque vous cliquez sur la case à cocher afin que vous puissiez utiliser cette fonction:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

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

ahmedcool166
la source
2

J'ai trouvé une réponse plus simple à ce problème. J'utilise simplement la logique inverse. Le code est en VB mais ce 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 est de ne pas avoir besoin de plusieurs événements.

Jimva
la source
1

Ce qui a fonctionné pour moi était CurrentCellDirtyStateChangeden combinaison avecdatagridView1.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();
}
betterave123
la source
1

Le code boucle dans DataGridView et vérifie si la colonne CheckBox est vérifié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;
        }
    }
}
Codeur E
la source
1

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;
        }
    }
}
daniele3004
la source
1

J'ai essayé quelques réponses à partir d'ici, mais j'ai toujours eu une sorte de problème (comme un double-clic ou l'utilisation du clavier). Donc, j'ai combiné certains d'entre eux et j'ai obtenu un comportement cohérent (ce n'est pas parfait, mais 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.

}
Félix Severo
la source
0

Pour ce faire lors de l'utilisation de devexpress xtragrid, 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 s'assurer 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 puisque le xtragrid ne fournit pas d'énumérateur, il est nécessaire d'utiliser une boucle for pour parcourir les lignes.

majjam
la source
0

La suppression du focus après les modifications de valeur de cellule permet aux valeurs de se mettre à jour dans DataGridView. Supprimez le focus en définissant 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);
    }

}
Branden Huggins
la source
0

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

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 .

David Ruiz
la source
0

Ben Voigt a trouvé la meilleure solution dans un commentaire-réponse ci-dessus:

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

Sérieusement, c'est TOUT ce dont vous avez besoin.

Roger M. Wilcox
la source