Jak wykryć zmianę zdarzenia DataGridView CheckBox?

90

Mam aplikację winForms i chcę wyzwolić kod, gdy pole wyboru osadzone w DataGridViewformancie jest zaznaczone / odznaczone. Każde wydarzenie, którego próbowałem

  1. Uruchamia się zaraz po CheckBoxkliknięciu, ale przed zmianą stanu zaznaczenia lub
  2. Uruchamia się tylko wtedy, gdy CheckBoxtraci ostrość

Nie mogę znaleźć zdarzenia uruchamiającego się natychmiast po zmianie stanu zaznaczenia.


Edytować:

To, co staram się osiągnąć, to to, że gdy zmienia się stan zaznaczenia CheckBoxjednej w jednej DataGridView, dane w dwóch innych się DataGridViewzmieniają. Jednak wszystkie zdarzenia, których użyłem, dane w innych siatkach zmieniają się dopiero po tym, jak CheckBoxw pierwszej DataGridViewtraci fokus.

PJW
źródło
2
Czy sprawdziłeś CurrentCellDirtyStateChangedwydarzenie?
Yograj Gupta
Nadal jest wykonywany tylko wtedy, gdy użytkownik „opuszcza” komórkę.
PJW
1
Oto artykuł MSDN na ten temat: msdn.microsoft.com/en-us/library/ ... podobny, ale nieco inny od odpowiedzi Killercam
David Hall

Odpowiedzi:

96

Aby obsłużyć zdarzenie DatGridViews CheckedChanged, musisz najpierw uruchomić CellContentClickpolecenie (które nie ma CheckBoxobecnego stanu es!), A następnie wywołać CommitEdit. To z kolei uruchomi CellValueChangedzdarzenie, którego możesz użyć do wykonania swojej pracy. To przeoczenie Microsoft . Zrób coś takiego jak następujące ...

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

Mam nadzieję, że to pomoże.

PS Sprawdź ten artykuł https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx

Księżycowy rycerz
źródło
5
Jest to dobre rozwiązanie, ale nie działa, jeśli użytkownik kliknie kilka razy, alternatywa została opublikowana poniżej stackoverflow.com/questions/11843488/ ...
56ka
1
Zdecydowanie sugerowałbym również NIE używanie tego rozwiązania w przypadku problemu z podwójnym kliknięciem. Należy wywołać funkcję EndEdit () ... znajdź link z @ 56ka i kliknij link do artykułu!
Łukasz
1
Nie spędziłem dużo czasu nad tym rozwiązaniem i jeśli rozwiązanie @ 56ka jest lepsze, to świetnie. Jednak nie jestem pewien, na czym DataGridViewCheckBoxpolega całe zamieszanie związane z dwukrotnym kliknięciem . To nie jest WPF i dwukrotne kliknięcie kontrolki nie powoduje zerwania żadnego powiązania danych, to WinForms. Podwójne kliknięcie może nie zaktualizować wizualnie kontrolki, ale niczego nie psuje iw tym przypadku być może poniższe rozwiązanie jest lepsze. Dzięki.
MoonKnight
Działa to doskonale, jeśli dodasz również ten sam kod z CellContentClickdo CellContentDoubleClick. CellMouseUpProgram zostanie uruchomiony, nawet jeśli komórka jest zaznaczona, ale pole wyboru nie jest zaznaczone - co nie jest pożądane.
otępiała zdobycz,
89

Znalazłem rozwiązanie @ Killercam, które działa, ale było trochę podejrzane, jeśli użytkownik kliknął dwukrotnie zbyt szybko. Nie jestem pewien, czy inni też to stwierdzili. Znalazłem inne rozwiązanie tutaj .

Używa datagridów CellValueChangedi CellMouseUp. Changhong wyjaśnia to

„Przyczyną tego jest to, że zdarzenie OnCellvalueChanged nie zostanie uruchomione, dopóki DataGridView nie uzna, że ​​zakończyłaś edycję. To ma sens dla kolumny TextBox, ponieważ OnCellvalueChanged nie [kłopocze] się uruchamianiem dla każdego uderzenia klawisza, ale nie [ ma sens] dla CheckBox. "

Oto jego przykład w akcji:

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

I kod informujący pole wyboru, że edycja jest zakończona po kliknięciu, zamiast czekać, aż użytkownik opuści pole:

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

Edycja: zdarzenie DoubleClick jest traktowane oddzielnie od zdarzenia MouseUp. Jeśli wykryte zostanie zdarzenie DoubleClick, aplikacja całkowicie zignoruje pierwsze zdarzenie MouseUp. Ta logika musi zostać dodana do zdarzenia CellDoubleClick oprócz zdarzenia 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
źródło
3
Napotkałem problem podwójnego kliknięcia zauważony przez osobę odpowiadającą, a ten działał znacznie lepiej niż pierwsze rozwiązanie, jeśli chodzi o poprawną obsługę.
Steve Ferguson
1
Napotkałem również problem podwójnego kliknięcia i to rozwiązanie go naprawiło.
Chris C
Kliknij przycisk „tutaj” i zapoznaj się z artykułem. Miałem ten sam problem z podwójnym kliknięciem.
Łukasz
4
Co się stanie, jeśli przełączysz przełącznik za pomocą spacji?
Halfgaar
1
Aby „rozwiązać” problem spacji, ustawiłem KeyPreviewna wartość true w formularzu i kiedy e.KeyCode == Keys.Spaceustawiłem e.Handled = true. Innymi słowy, właśnie wyłączyłem edycję klawiatury.
Halfgaar
9

Rozwiązanie jsturtevants działało świetnie. Jednak zdecydowałem się wykonać przetwarzanie w zdarzeniu EndEdit. Preferuję to podejście (w mojej aplikacji), ponieważ w przeciwieństwie do zdarzenia CellValueChanged zdarzenie EndEdit nie jest uruchamiane podczas wypełniania siatki.

Oto mój kod (którego część została skradziona z 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
źródło
3
Dobra odpowiedź, ale lepiej jest użyć CellContentClickzamiast tego, CellMouseUpponieważ ta druga zostanie wywołana, gdy użytkownik kliknie w dowolnym miejscu w komórce, podczas gdy pierwsza jest wywoływana tylko po kliknięciu pola wyboru.
Jamie Kitson
6

Obsługuje również aktywację klawiatury.

    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
źródło
5

Oto kod:

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
źródło
2
Ta odpowiedź zawiera poprawną odpowiedź, która obsługuje zarówno interakcje z myszą, jak i klawiaturą oraz powtarzane interakcje bez opuszczania komórki. Ale potrzebny jest tylko ostatni handler - wywołanie CommitEditz CurrentCellDirtyStateChangedjest całym rozwiązaniem.
Ben Voigt
4

po Killercam'answer, My code

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

i :

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ê
źródło
2

Chodzi o edycję komórki, problem polegający na tym, że komórka w rzeczywistości nie była edytowana, więc musisz zapisać zmiany komórki lub wiersza, aby uzyskać zdarzenie po kliknięciu pola wyboru, aby móc użyć tej funkcji:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

dzięki temu możesz go użyć nawet z innym wydarzeniem.

ahmedcool166
źródło
2

Znalazłem prostszą odpowiedź na ten problem. Po prostu używam logiki odwrotnej. Kod jest w VB, ale niewiele różni się od 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

Jedną z najlepszych rzeczy w tym jest brak konieczności organizowania wielu wydarzeń.

Jimva
źródło
1

To, co zadziałało dla mnie, było CurrentCellDirtyStateChangedw połączeniu zdatagridView1.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();
}
burak123
źródło
1

Kod zapętli się w DataGridView i sprawdzi, czy kolumna CheckBox jest zaznaczona

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;
        }
    }
}
E Coder
źródło
1

W przypadku CellContentClick możesz skorzystać z tej strategii:

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
źródło
1

Wypróbowałem kilka odpowiedzi stąd, ale zawsze miałem jakiś problem (na przykład podwójne kliknięcie lub użycie klawiatury). Więc połączyłem niektóre z nich i uzyskałem spójne zachowanie (nie jest idealne, ale działa poprawnie).

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
źródło
0

Aby to zrobić podczas korzystania z devexpress xtragrid, konieczne jest obsłużenie zdarzenia EditValueChanged odpowiedniego elementu repozytorium, jak opisano tutaj . Ważne jest również wywołanie metody gridView1.PostEditor (), aby upewnić się, że zmieniona wartość została opublikowana. Oto realizacja:

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

Zauważ, że ponieważ xtragrid nie zapewnia modułu wyliczającego, konieczne jest użycie pętli for do iteracji po wierszach.

majjam
źródło
0

Usunięcie fokusu po zmianie wartości komórki umożliwia aktualizację wartości w DataGridView. Usuń fokus, ustawiając CurrentCell na 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
źródło
0

Możesz wymusić na komórce zatwierdzenie wartości zaraz po kliknięciu pola wyboru, a następnie przechwyceniu zdarzenia CellValueChanged . CurrentCellDirtyStateChanged pożarów jak najszybciej kliknij pole wyboru.

U mnie działa następujący kod:

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

Następnie możesz wstawić kod w zdarzeniu CellValueChanged .

David Ruiz
źródło
0

Ben Voigt znalazł najlepsze rozwiązanie w komentarzu-odpowiedzi powyżej:

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

Poważnie, to wszystko, czego potrzebujesz.

Roger M. Wilcox
źródło