Wie erkenne ich eine DataGridView CheckBox-Ereignisänderung?


89

Ich habe eine Winforms-App und möchte Code auslösen, wenn ein in ein DataGridViewSteuerelement eingebettetes Kontrollkästchen aktiviert / deaktiviert ist. Jedes Ereignis habe ich auch versucht

  1. Wird CheckBoxausgelöst, sobald auf das geklickt wird, bevor sich sein aktivierter Status ändert, oder
  2. Wird nur CheckBoxausgelöst, wenn der Fokus verloren geht

Ich kann anscheinend kein Ereignis finden, das unmittelbar nach der Änderung des überprüften Status ausgelöst wird.


Bearbeiten:

Was ich versuche zu erreichen, ist, dass sich die Daten in zwei anderen ändern , wenn sich der überprüfte Zustand von a CheckBoxin one ändert. Bei allen Ereignissen, die ich verwendet habe, ändern sich die Daten in den anderen Gittern erst, nachdem der Fokus im ersten verloren gegangen ist. DataGridViewDataGridViewCheckBoxDataGridView


2
Haben Sie CurrentCellDirtyStateChangedEvent überprüft ?
Yograj Gupta

Wird immer noch nur ausgeführt, wenn der Benutzer die Zelle 'verlässt'.
PJW

1
Hier ist der MSDN-Artikel dazu: msdn.microsoft.com/en-us/library/… ähnlich, aber ein wenig anders als Killercams Antwort
David Hall

Antworten:


96

Um das DatGridViews- CheckedChangedEreignis zu behandeln, müssen Sie zuerst das CellContentClickFeuer auslösen (das nicht den CheckBoxaktuellen Status hat!) Und dann aufrufen CommitEdit. Dies löst wiederum das CellValueChangedEreignis aus, mit dem Sie Ihre Arbeit erledigen können. Dies ist ein Versehen von Microsoft . Machen Sie etwas wie das Folgende ...

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

Ich hoffe das hilft.

PS Überprüfen Sie diesen Artikel https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx


4
Dies ist eine gute Lösung, funktioniert aber nicht, wenn der Benutzer mehrmals klickt. Eine Alternative wurde unter stackoverflow.com/questions/11843488/…
56ka

1
Ich würde auch dringend empfehlen, diese Lösung NICHT für das Doppelklickproblem zu verwenden. Die EndEdit () -Funktion muss aufgerufen werden ... finde den Link von @ 56ka und klicke auf den Artikel-Link!
Luke

1
Ich habe nicht lange mit dieser Lösung verbracht und wenn die Lösung von @ 56ka eine bessere ist, großartig. Ich bin mir jedoch nicht sicher, wie viel Aufhebens um das Doppelklicken auf a DataGridViewCheckBoxist. Dies ist kein WPF und ein Doppelklick auf das Steuerelement unterbricht keine Datenbindung, sondern WinForms. Ein Doppelklick auf das Steuerelement visuell nicht aktualisieren kann aber nicht brechen nichts und in diesem Fall vielleicht die Lösung unten ist die bessere. Vielen Dank.
MoonKnight

Dies funktioniert perfekt, wenn Sie denselben Code auch von CellContentClickin hinzufügen CellContentDoubleClick. CellMouseUpDies wird auch dann ausgelöst, wenn die Zelle ausgewählt ist, aber das Kontrollkästchen nicht aktiviert ist - was nicht erwünscht ist.
Torpide Beute

87

Ich fand, dass @ Killercams Lösung funktioniert, war aber etwas zwielichtig, wenn der Benutzer zu schnell doppelklickte. Ich bin mir nicht sicher, ob andere das auch gefunden haben. Ich habe hier eine andere Lösung gefunden .

Es verwendet die Datagrid's CellValueChangedund CellMouseUp. Changhong erklärt das

"Der Grund dafür ist, dass das OnCellvalueChanged-Ereignis erst ausgelöst wird, wenn DataGridView glaubt, dass Sie die Bearbeitung abgeschlossen haben. Dies macht Sinn für eine TextBox-Spalte, da OnCellvalueChanged sich nicht die Mühe machen würde, bei jedem Tastendruck zu feuern, aber nicht [ Sinn machen] für eine CheckBox. "

Hier ist es in Aktion von seinem Beispiel:

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

Und der Code, der dem Kontrollkästchen mitteilt, dass die Bearbeitung beim Klicken abgeschlossen ist, anstatt zu warten, bis der Benutzer das Feld verlässt:

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

3
Ich bin auf das vom Responder festgestellte Doppelklickproblem gestoßen, und dieses hat viel besser funktioniert als die erste Lösung, um das richtig zu handhaben.
Steve Ferguson

1
Ich bin auch auf das Doppelklick-Problem gestoßen, und diese Lösung hat es behoben.
Chris C

Klicken Sie auf die Schaltfläche "Hier" und lesen Sie den Artikel. Ich hatte das gleiche Problem mit dem Doppelklick.
Luke

4
Was ist, wenn Sie den Schalter mit der Leertaste umschalten?
Halfgaar

1
Um das Problem mit der Leertaste zu beheben, habe ich KeyPreviewim Formular den Wert true festgelegt und wann e.KeyCode == Keys.Spacefestgelegt e.Handled = true. Mit anderen Worten, ich habe gerade die Tastaturbearbeitung deaktiviert.
Halfgaar

9

Die Lösung von jsturtevants hat großartig funktioniert. Ich habe mich jedoch für die Verarbeitung im EndEdit-Ereignis entschieden. Ich bevorzuge diesen Ansatz (in meiner Anwendung), da das EndEdit-Ereignis im Gegensatz zum CellValueChanged-Ereignis nicht ausgelöst wird, während Sie das Raster füllen.

Hier ist mein Code (von dem ein Teil von jsturtevant gestohlen wurde:

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
Gute Antwort, aber es ist vorzuziehen, CellContentClickanstatt zu verwenden, CellMouseUpda Letzteres aufgerufen wird, wenn der Benutzer irgendwo in der Zelle klickt, während Ersteres nur aufgerufen wird, wenn das Kontrollkästchen aktiviert ist.
Jamie Kitson

6

Dies übernimmt auch die Tastaturaktivierung.

    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

Hier ist ein 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);
    }
}

2
Diese Antwort enthält die richtige Antwort, die sowohl Maus- und Tastaturinteraktionen als auch wiederholte Interaktionen behandelt, ohne die Zelle zu verlassen. Es wird jedoch nur der letzte Handler benötigt - das Aufrufen CommitEditvon CurrentCellDirtyStateChangedist die gesamte Lösung.
Ben Voigt

4

nach Killercam'answer, Mein Code

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

und :

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

Es geht nur darum, die Zelle zu bearbeiten. Das Problem ist, dass die Zelle nicht bearbeitet wurde. Sie müssen also die Änderungen der Zelle oder der Zeile speichern, um das Ereignis zu erhalten, wenn Sie auf das Kontrollkästchen klicken, damit Sie diese Funktion verwenden können:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

Damit können Sie es auch bei einem anderen Ereignis verwenden.


2

Ich habe eine einfachere Antwort auf dieses Problem gefunden. Ich benutze einfach die umgekehrte Logik. Der Code ist in VB, unterscheidet sich jedoch nicht wesentlich von 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

Eines der besten Dinge dabei ist, dass nicht mehrere Ereignisse erforderlich sind.


1

Was für mich funktionierte, war CurrentCellDirtyStateChangedin Kombination mitdatagridView1.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

Der Code wird in DataGridView wiederholt und überprüft, ob die CheckBox-Spalte aktiviert ist

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

Für den Fall CellContentClick können Sie diese Strategie verwenden:

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

Ich habe einige Antworten von hier aus versucht, aber ich hatte immer ein Problem (wie Doppelklick oder Verwendung der Tastatur). Also habe ich einige davon kombiniert und ein konsistentes Verhalten erhalten (es ist nicht perfekt, funktioniert aber richtig).

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

Um dies bei Verwendung des devexpress xtragrid zu tun, muss das EditValueChanged- Ereignis eines entsprechenden Repository-Elements wie hier beschrieben behandelt werden . Es ist auch wichtig, die Methode gridView1.PostEditor () aufzurufen, um sicherzustellen, dass der geänderte Wert gebucht wurde. Hier ist eine Implementierung:

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

Beachten Sie, dass es erforderlich ist, eine for-Schleife zu verwenden, um über Zeilen zu iterieren, da das xtragrid keinen Enumerator bereitstellt.


0

Durch Entfernen des Fokus nach Änderungen der Zellenwerte können die Werte in DataGridView aktualisiert werden. Entfernen Sie den Fokus, indem Sie die CurrentCell auf null setzen.

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

Sie können die Zelle zwingen, den Wert festzuschreiben, sobald Sie auf das Kontrollkästchen klicken und dann das CellValueChanged- Ereignis abfangen . Der CurrentCellDirtyStateChanged wird ausgelöst, sobald Sie auf das Kontrollkästchen klicken.

Der folgende Code funktioniert für mich:

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

Anschließend können Sie Ihren Code in das CellValueChanged- Ereignis einfügen .


0

Ben Voigt fand die beste Lösung in einer Kommentar-Antwort oben:

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

Im Ernst, das ist alles was Sie brauchen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.