Bagaimana cara mendeteksi perubahan acara DataGridView CheckBox?

91

Saya memiliki aplikasi winforms dan ingin memicu beberapa kode ketika kotak centang yang disematkan dalam DataGridViewkontrol dicentang / tidak dicentang. Setiap acara saya coba juga

  1. Memicu segera setelah CheckBoxdiklik tetapi sebelum status yang dicentang berubah, atau
  2. Memicu hanya setelah CheckBoxkehilangan fokusnya

Sepertinya saya tidak dapat menemukan peristiwa yang terpicu segera setelah status yang dicentang berubah.


Edit:

Apa yang saya coba capai adalah ketika status yang dicentang dari a CheckBoxdalam satu DataGridViewberubah, data di dua lainnya DataGridViewberubah. Namun semua kejadian yang telah saya gunakan, data di kisi lain hanya berubah setelah CheckBoxdi kisi pertama DataGridViewkehilangan fokus.

PJW
sumber
2
Apakah Anda sudah memeriksa CurrentCellDirtyStateChangedAcara?
Yograj Gupta
Masih hanya dijalankan saat pengguna 'meninggalkan' sel.
PJW
1
Berikut adalah artikel MSDN tentang ini: msdn.microsoft.com/en-us/library/… mirip tetapi sedikit berbeda dengan jawaban Killercam
David Hall

Jawaban:

97

Untuk menangani peristiwa DatGridViews, CheckedChangedpertama-tama Anda harus CellContentClickmengaktifkan to (yang tidak memiliki status CheckBoxes saat ini!) Lalu panggil CommitEdit. Ini pada gilirannya akan mengaktifkan CellValueChangedperistiwa yang dapat Anda gunakan untuk melakukan pekerjaan Anda. Ini adalah pengawasan dari Microsoft . Lakukan beberapa hal seperti berikut ...

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

Saya harap ini membantu.

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

Ksatria bulan
sumber
5
Ini adalah solusi yang baik tetapi tidak berfungsi jika pengguna mengklik beberapa kali, alternatif telah diposting di bawah stackoverflow.com/questions/11843488/…
56ka
1
Saya juga sangat menyarankan TIDAK menggunakan solusi ini untuk masalah klik ganda. Fungsi EndEdit () perlu dipanggil ... temukan link dari @ 56ka dan klik link artikel!
Lukas
1
Saya tidak menghabiskan waktu lama untuk solusi ini dan jika solusi @ 56ka lebih baik, bagus. Namun, saya tidak yakin apa yang diributkan tentang mengklik ganda a DataGridViewCheckBox. Ini bukan WPF dan mengklik dua kali kontrol tidak melanggar pengikatan data apa pun, ini adalah WinForms. Mengklik dua kali mungkin tidak memperbarui kontrol secara visual tetapi tidak merusak apa pun dan dalam kasus ini mungkin solusi di bawah ini adalah yang lebih baik. Terima kasih.
MoonKnight
Ini berfungsi dengan sempurna jika Anda menambahkan kode yang sama dari CellContentClickke CellContentDoubleClickjuga. CellMouseUpis akan aktif bahkan jika sel dipilih tetapi kotak centang tidak diklik - yang bukan merupakan perilaku yang diinginkan.
mangsa
89

Saya menemukan solusi @ Killercam untuk bekerja tetapi agak cerdik jika pengguna mengklik dua kali terlalu cepat. Tidak yakin apakah orang lain juga menemukan kasus itu. Saya menemukan solusi lain di sini .

Ini menggunakan datagrid CellValueChangeddan CellMouseUp. Changhong menjelaskan itu

"Alasannya adalah peristiwa OnCellvalueChanged tidak akan diaktifkan sampai DataGridView mengira Anda telah menyelesaikan pengeditan. Hal ini masuk akal untuk Kolom TextBox, karena OnCellvalueChanged tidak akan [repot] untuk mengaktifkan setiap pemogokan kunci, tetapi tidak [ masuk akal] untuk Kotak Centang. "

Ini dia tindakan dari contohnya:

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

Dan kode untuk memberi tahu kotak centang itu selesai mengedit saat diklik, alih-alih menunggu sampai pengguna meninggalkan bidang:

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

Edit: Peristiwa DoubleClick diperlakukan terpisah dari peristiwa MouseUp. Jika peristiwa DoubleClick terdeteksi, aplikasi akan mengabaikan sepenuhnya peristiwa MouseUp pertama. Logika ini perlu ditambahkan ke acara CellDoubleClick selain acara 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.dll
sumber
3
Saya mengalami masalah klik dua kali yang dicatat oleh responden, dan yang ini bekerja jauh lebih baik daripada solusi pertama dalam menanganinya dengan benar.
Steve Ferguson
1
Saya juga mengalami masalah klik dua kali dan solusi ini memperbaikinya.
Chris C
Klik tombol 'di sini' dan lihat artikelnya. Saya memiliki masalah yang sama dengan klik ganda.
Lukas
4
Bagaimana jika Anda mengaktifkan sakelar dengan bilah spasi?
Halfgaar
1
Untuk 'memperbaiki' masalah bilah spasi, saya setel KeyPreviewke true pada formulir dan kapan e.KeyCode == Keys.Space, setel e.Handled = true. Dengan kata lain, saya baru saja menonaktifkan pengeditan keyboard.
Halfgaar
9

Solusi jsturtevants bekerja dengan baik. Namun, saya memilih untuk melakukan pemrosesan di acara EndEdit. Saya lebih suka pendekatan ini (dalam aplikasi saya) karena, tidak seperti acara CellValueChanged, acara EndEdit tidak menyala saat Anda mengisi kisi.

Ini kode saya (sebagian dicuri dari 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
sumber
3
Jawaban yang bagus, tetapi lebih disukai untuk digunakan CellContentClickdaripada CellMouseUpkarena yang terakhir akan dipanggil saat pengguna mengklik di mana saja di dalam sel sedangkan yang pertama hanya dipanggil saat kotak centang diklik.
Jamie Kitson
6

Ini juga menangani aktivasi keyboard.

    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
sumber
5

Berikut beberapa kode:

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
sumber
2
Jawaban ini berisi jawaban yang benar, yang menangani interaksi mouse dan keyboard, dan interaksi berulang tanpa meninggalkan sel. Tetapi hanya penangan terakhir yang dibutuhkan - menelepon CommitEditdari CurrentCellDirtyStateChangedadalah solusi keseluruhan.
Ben Voigt
4

mengikuti jawaban Killercam, kode saya

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

dan:

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ê
sumber
2

Ini semua tentang mengedit sel, masalahnya adalah sel tidak diedit sebenarnya, jadi Anda perlu menyimpan Perubahan sel atau baris untuk mendapatkan acara saat Anda mengklik kotak centang sehingga Anda dapat menggunakan fungsi ini:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

dengan ini, Anda dapat menggunakannya bahkan dengan acara yang berbeda.

ahmedcool166
sumber
2

Saya telah menemukan jawaban yang lebih sederhana untuk masalah ini. Saya hanya menggunakan logika terbalik. Kode ada di VB tetapi tidak jauh berbeda dengan 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

Salah satu hal terbaik tentang ini adalah tidak perlu mengadakan banyak acara.

Jimva
sumber
1

Apa yang berhasil bagi saya adalah CurrentCellDirtyStateChangedkombinasi dengandatagridView1.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();
}
bit123
sumber
1

Kode akan berputar di DataGridView dan Akan memeriksa apakah Kolom Kotak Centang Dicentang

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
sumber
1

Dalam acara CellContentClick Anda dapat menggunakan strategi ini:

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
sumber
1

Saya sudah mencoba beberapa jawaban dari sini, tetapi saya selalu mengalami masalah (seperti mengklik dua kali atau menggunakan keyboard). Jadi, saya menggabungkan beberapa di antaranya dan mendapatkan perilaku yang konsisten (tidak sempurna, tetapi berfungsi dengan baik).

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
sumber
0

Untuk melakukan ini saat menggunakan devexpress xtragrid, perlu untuk menangani acara EditValueChanged item repositori terkait seperti yang dijelaskan di sini . Penting juga untuk memanggil metode gridView1.PostEditor () untuk memastikan nilai yang diubah telah diposting. Berikut implementasinya:

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

Perhatikan bahwa karena xtragrid tidak menyediakan enumerator, maka perlu menggunakan loop for untuk mengulang baris.

majjam
sumber
0

Menghapus fokus setelah perubahan nilai sel memungkinkan nilai untuk diperbarui di DataGridView. Hapus fokus dengan mengatur CurrentCell ke 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
sumber
0

Anda bisa memaksa sel untuk memasukkan nilai segera setelah Anda mengklik kotak centang dan kemudian menangkap acara CellValueChanged . The CurrentCellDirtyStateChanged kebakaran segera setelah Anda klik kotak centang.

Kode berikut berfungsi untuk saya:

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

Anda kemudian dapat memasukkan kode Anda di acara CellValueChanged .

David Ruiz
sumber
0

Ben Voigt menemukan solusi terbaik dalam balasan komentar di atas:

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

Serius, hanya itu yang Anda butuhkan.

Roger M. Wilcox
sumber