Peristiwa CheckedListBox mana yang terpicu setelah item dicentang?


96

Saya memiliki CheckedListBox di mana saya ingin acara setelah item dicentang sehingga saya dapat menggunakan CheckedItems dengan status baru.

Karena ItemChecked diaktifkan sebelum CheckedItems diperbarui, itu tidak akan berfungsi di luar kotak.

Jenis metode atau acara apa yang dapat saya gunakan untuk diberi tahu saat CheckedItems diperbarui?

Jawaban:


89

Anda dapat menggunakan ItemCheckacara tersebut, jika Anda juga memeriksa status baru dari item yang sedang diklik. Ini tersedia dalam acara args, sebagai e.NewValue. Jika NewValuedicentang, sertakan item saat ini bersama dengan koleksi yang sesuai dalam logika Anda:

    private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
    {                     
        List<string> checkedItems = new List<string>();
        foreach (var item in checkedListBox1.CheckedItems)
            checkedItems.Add(item.ToString());

        if (e.NewValue == CheckState.Checked)
            checkedItems.Add(checkedListBox1.Items[e.Index].ToString());
        else
            checkedItems.Remove(checkedListBox1.Items[e.Index].ToString());

        foreach (string item in checkedItems)
        {
            ...
        }
    }

Sebagai contoh lain, untuk menentukan apakah koleksi akan kosong setelah item ini (tidak) dicentang:

private void ListProjects_ItemCheck(object sender, ItemCheckEventArgs args)
{
    if (ListProjects.CheckedItems.Count == 1 && args.NewValue == CheckState.Unchecked)
        // The collection is about to be emptied: there's just one item checked, and it's being unchecked at this moment
        ...
    else
        // The collection will not be empty once this click is handled
        ...
}

3
di bagian pertama untuk masing-masing, kita mungkin perlu menambahkan satu jika kondisi ..if not item = checkedListBox1.Items[e.Index].ToString()
Lenin Raj Rajasekaran

8
Masalahnya adalah bahwa event ItemCheck dijalankan sebelum cek diproses. Solusi Anda akan melibatkan menjaga daftar Anda sendiri, pada dasarnya menduplikasi kode standar. Saran pertama Dunc (Delayed execution on ItemCheck) adalah jawaban paling bersih untuk pertanyaan phq, karena tidak memerlukan penanganan tambahan.
Berend Engelbrecht

35

Ada banyak posting StackOverflow terkait tentang ini ... Selain solusi Branimir , berikut dua yang lebih sederhana:

Eksekusi tertunda di ItemCheck (juga di sini ):

    void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
    {
        this.BeginInvoke((MethodInvoker) (
            () => Console.WriteLine(checkedListBox1.SelectedItems.Count)));
    }

Menggunakan acara MouseUp :

    void checkedListBox1_MouseUp(object sender, MouseEventArgs e)
    {
        Console.WriteLine(checkedListBox1.SelectedItems.Count);
    }

Saya lebih suka opsi pertama, karena yang kedua akan menghasilkan positif palsu (mis. Menembak terlalu sering).


13
Metode kedua juga akan melewatkan item yang diperiksa atau tidak dicentang melalui keyboard.

1
BeginInvoke persis seperti yang saya butuhkan karena acara saya sebenarnya memanggil antarmuka, yang tidak tahu jenis kontrol apa yang dihadapinya. Jawaban yang diterima hanya berfungsi dalam kasus-kasus sementara logika dapat dijalankan dalam pengendali peristiwa, atau sesuatu yang dipanggil langsung dari pengendali peristiwa. Ini tidak terjadi pada saya. Terima kasih untuk solusi yang luar biasa namun sederhana ini.
Jesse

Thx, opsi pertama dengan BeginInvoke berfungsi untuk saya. Mungkin komentar konyol sobat .. tapi kenapa BUG ini diberitakan dengan topik mulai tahun 2010 tidak terselesaikan di tahun 2018 ??
Goodies

1
@ Goodies Setuju, meskipun saya rasa itu bisa merusak banyak kode jika Microsoft mengubah perilakunya sekarang. The docs secara eksplisit menyatakan The check state is not updated until after the ItemCheck event occurs. Acara yang berbeda atau solusi yang tidak sewenang-wenang akan menjadi IMO yang bagus.
Dunc

24

Saya mencoba ini dan berhasil:

private void clbOrg_ItemCheck(object sender, ItemCheckEventArgs e)
{
    CheckedListBox clb = (CheckedListBox)sender;
    // Switch off event handler
    clb.ItemCheck -= clbOrg_ItemCheck;
    clb.SetItemCheckState(e.Index, e.NewValue);
    // Switch on event handler
    clb.ItemCheck += clbOrg_ItemCheck;

    // Now you can go further
    CallExternalRoutine();        
}

8
Ini! ... seharusnya jawaban yang benar, yang sayangnya. Ini adalah peretasan konyol yang berhasil karena seseorang di M $ lupa mengimplementasikan ItemCheckedacara tersebut, dan tidak ada yang pernah membahas bahwa acara tersebut tidak ada.
RLH

Meskipun menurut definisi bukan bug, saya pikir ini harus diterapkan, jika Anda setuju pertimbangkan untuk mendukung laporan bug ini dengan mengklik +1: connect.microsoft.com/VisualStudio/feedback/details/1759293
SCBuergel.eth

@ Sebastian - jangan minta perbaikan di sini. Setiap "perbaikan" ini akan merusak solusi yang ada. Jika ada dua peristiwa: ItemChecking, ItemChecked, maka Anda bisa menggunakan salah satu yang terakhir. Tetapi jika hanya satu yang diimplementasikan ( ItemCheck) itu melakukan hal-hal dengan benar, yaitu mengaktifkan peristiwa sebelum nilai diperiksa dengan nilai baru dan indeks disediakan sebagai parameter. Siapa pun yang menginginkan acara "setelah perubahan", mereka dapat menggunakan cara di atas. Jika menyarankan sesuatu kepada Microsoft kemudian sarankan acara baru ItemChecked , bukan mengubah yang sudah ada: lihat jawaban
diimdeep

Seperti ini, tetapi satu alternatif kecil yang saya gunakan sepanjang waktu hanyalah menyetel semacam tanda "lewati" sehingga SetItemCheckState tidak memicu kembali peristiwa yang sama. Entah global sederhana, atau yang ingin saya lakukan adalah memastikan tagnya. misalnya, bungkus tindakan dalam If myCheckListBox.Tag! = null, lalu sebagai ganti Event Delete \ Add, cukup setel tag ke sesuatu (bahkan string kosong) dan kemudian kembali ke null untuk mengaktifkannya kembali.
da_jokker

10

Berasal dari CheckedListBoxdan terapkan

/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.CheckedListBox.ItemCheck"/> event.
/// </summary>
/// <param name="ice">An <see cref="T:System.Windows.Forms.ItemCheckEventArgs"/> that contains the event data.
///                 </param>
protected override void OnItemCheck(ItemCheckEventArgs e)
{           
    base.OnItemCheck(e);

    EventHandler handler = AfterItemCheck;
    if (handler != null)
    {
        Delegate[] invocationList = AfterItemCheck.GetInvocationList();
        foreach (var receiver in invocationList)
        {
            AfterItemCheck -= (EventHandler) receiver;
        }

        SetItemCheckState(e.Index, e.NewValue);

        foreach (var receiver in invocationList)
        {
            AfterItemCheck += (EventHandler) receiver;
        }
    }
    OnAfterItemCheck(EventArgs.Empty);
}

public event EventHandler AfterItemCheck;

public void OnAfterItemCheck(EventArgs e)
{
    EventHandler handler = AfterItemCheck;
    if (handler != null)
        handler(this, e);
}

Itu banyak kode tambahan yang bisa Anda lewati saat menggunakan BeginInvokesolusi dari jawaban kedua.
Łukasƨ Fronczyk

4

Meskipun tidak ideal, Anda bisa menghitung CheckedItems menggunakan argumen yang diteruskan ke ItemCheckacara. Jika Anda melihat contoh ini di MSDN , Anda dapat mengetahui apakah item yang baru diubah telah dicentang atau tidak, yang membuat Anda berada dalam posisi yang sesuai untuk bekerja dengan item tersebut.

Anda bahkan dapat membuat acara baru yang aktif setelah item dicentang, yang akan memberi Anda apa yang Anda inginkan jika Anda mau.


1
Apakah Anda punya ide khusus tentang bagaimana acara baru ini dapat dibuat, bagaimana saya bisa tahu ketika CheckedItems telah diperbarui setelah acara ItemChecke?
hultqvist

4

Setelah beberapa tes, saya bisa melihat bahwa acara SelectedIndexChanged dipicu setelah acara ItemCheck. Pertahankan properti CheckOnClick True

Pengkodean terbaik


Anda benar, ini cara termudah. Tapi itu masih seperti retasan, karena itu perilaku yang tidak berdokumen dan TIDAK DIHARAPKAN. Setiap mahasiswa baru di Microsoft mungkin berpikir: oh baiklah, mengapa mengaktifkan SelectedIndexChanged ketika hanya Checkstate yang berubah. Mari kita optimalkan itu. Dan Bang membuka kode Anda :(
Rolf

Selain itu, SelectedIndexChanged tidak diaktifkan saat Anda mengubah status pemeriksaan secara terprogram.
Rolf

1
Dan itu tidak menyala ketika Anda mengubah status cek dengan tombol Spasi. Salah menggunakan ini.
Elmue

2

Ini berfungsi, meskipun tidak yakin seberapa elegannya!

Private Sub chkFilters_Changed(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkFilters.ItemCheck
    Static Updating As Boolean
    If Updating Then Exit Sub
    Updating = True

    Dim cmbBox As CheckedListBox = sender
    Dim Item As ItemCheckEventArgs = e

    If Item.NewValue = CheckState.Checked Then
        cmbBox.SetItemChecked(Item.Index, True)
    Else
        cmbBox.SetItemChecked(Item.Index, False)
    End If

    'Do something with the updated checked box
    Call LoadListData(Me, False)

    Updating = False
End Sub

1

Tidak tahu apakah ini berlaku tetapi saya ingin menggunakan kotak daftar periksa untuk memfilter hasil. Jadi saat pengguna mencentang dan menghapus centang item, saya ingin daftar menampilkan \ sembunyikan item.

Saya mengalami beberapa masalah yang membawa saya ke posting ini. Hanya ingin berbagi bagaimana saya melakukannya tanpa sesuatu yang istimewa.

Catatan: Saya memiliki CheckOnClick = true tetapi mungkin masih akan berfungsi tanpa

Acara yang saya gunakan adalah " SelectedIndexChanged "

pencacahan yang saya gunakan adalah " .CheckedItems "

Ini memberikan hasil yang saya pikir bisa kita harapkan. Sederhananya, itu bermuara pada ....

private void clb1_SelectedIndexChanged(object sender, EventArgs e)
{
   // This just spits out what is selected for testing
   foreach (string strChoice in clb1.CheckedItems)
   {
      listBox1.Items.Add(strChoice);
   }

   //Something more like what I'm actually doing
   foreach (object myRecord in myRecords)
   {
        if (clb1.CheckItems.Contains(myRecord["fieldname"])
        {
            //Display this record
        }
   }

}

SelectedIndexChanged tidak aktif saat pengguna mengubah status pemeriksaan dengan tombol Spasi.
Elmue

SelectedIndexChanged tidak diaktifkan saat memanggil SetItemChecked untuk memeriksa atau menghapus centang item dalam kode.
bkqc

1

Dengan asumsi Anda ingin mempertahankan argumen dari ItemChecktetapi mendapatkan pemberitahuan setelah model diubah, akan terlihat seperti itu:

CheckedListBox ctrl = new CheckedListBox();
ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e)));

Dimana CheckedItemsChangedbisa:

private void CheckedItemsChanged(object sender, EventArgs e)
{
    DoYourThing();
}

0

Saya mencoba ini dan berhasil:

    private List<bool> m_list = new List<bool>();
    private void Initialize()
    {
        for(int i=0; i < checkedListBox1.Items.Count; i++)
        {
            m_list.Add(false);
        }
    }

    private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
    {
        if (e.NewValue == CheckState.Checked)
        {
            m_list[e.Index] = true;
            checkedListBox1.SetItemChecked(e.Index, true);
        }
        else
        {
            m_list[e.Index] = false;
            checkedListBox1.SetItemChecked(e.Index, false);
        }
    }

tentukan dengan indeks daftar.


-1

Saya menggunakan Timer untuk mengatasi masalah ini. Aktifkan pengatur waktu melalui acara ItemCheck. Ambil tindakan dalam acara Tik Pengatur Waktu.

Ini berfungsi baik item diperiksa melalui klik mouse atau dengan menekan Space-Bar. Kami akan memanfaatkan fakta bahwa item yang baru saja dicentang (atau tidak dicentang) selalu merupakan Item yang Dipilih.

Interval Pengatur Waktu bisa serendah 1. Pada saat event Tick dimunculkan, status baru yang Dicentang akan ditetapkan.

Kode VB.NET ini menunjukkan konsepnya. Ada banyak variasi yang bisa Anda terapkan. Anda mungkin ingin meningkatkan Interval Pengatur Waktu untuk memungkinkan pengguna mengubah status pemeriksaan pada beberapa item sebelum mengambil tindakan. Kemudian di acara Tick, lakukan pengoperan berurutan dari semua Item dalam Daftar atau gunakan koleksi CheckedItems untuk mengambil tindakan yang sesuai.

Itu sebabnya kami pertama-tama menonaktifkan Timer di acara ItemCheck. Nonaktifkan lalu Aktifkan menyebabkan periode Interval dimulai kembali.

Private Sub ckl_ItemCheck(ByVal sender As Object, _
                          ByVal e As System.Windows.Forms.ItemCheckEventArgs) _
    Handles ckl.ItemCheck

tmr.Enabled = False
tmr.Enabled = True

End Sub


Private Sub tmr_Tick(ByVal sender As System.Object, _
                     ByVal e As System.EventArgs) _
    Handles tmr.Tick

tmr.Enabled = False
Debug.Write(ckl.SelectedIndex)
Debug.Write(": ")
Debug.WriteLine(ckl.GetItemChecked(ckl.SelectedIndex).ToString)

End Sub

1
Terima kasih untuk berbagi. Di sisi lain, mungkin Anda dapat mempelajari solusi yang lebih baik dari jawaban lain. Menggunakan Timer relatif rumit dan dalam hal ini alat yang salah untuk pekerjaan tersebut, karena Anda sebenarnya sudah mendapatkan nilai baru sebagai parameter. Jadi, Anda dapat menggunakan jawaban ini untuk solusi satu kali atau yang ini untuk solusi sistematis. Konversikan dari C # ke VB menggunakan salah satu alat konversi online.
miroxlav

-1

Dalam perilaku normal, ketika kita memeriksa satu item, status pemeriksaan item akan berubah sebelum penanganan kejadian dimunculkan. Tetapi CheckListBox memiliki perilaku yang berbeda: Pengendali kejadian dimunculkan sebelum status pemeriksaan item berubah dan itu membuatnya sulit untuk memperbaiki pekerjaan kita.

Menurut pendapat saya, untuk mengatasi masalah ini, kita harus menunda pengendali acara.

private void _clb_ItemCheck(object sender, ItemCheckEventArgs e) {
 // Defer event handler execution
 Task.Factory.StartNew(() => {
     Thread.Sleep(1000);
     // Do your job at here
 })
 .ContinueWith(t => {
     // Then update GUI at here
 },TaskScheduler.FromCurrentSynchronizationContext());}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.