Bir öğe kontrol edildikten sonra hangi CheckedListBox olayı tetiklenir?


96

CheckedItems'i yeni durumla kullanabilmem için bir öğe kontrol edildikten sonra bir olay olmasını istediğim bir CheckedListBox'ım var .

ItemChecked, CheckedItems güncellenmeden önce tetiklendiğinden, kutudan çıkmaz.

CheckedItems güncellendiğinde bildirim almak için ne tür bir yöntem veya olay kullanabilirim?

Yanıtlar:


89

ItemCheckEtkinliği, tıklanan öğenin yeni durumunu da kontrol ederseniz kullanabilirsiniz . Bu, olay argümanlarında mevcuttur e.NewValue. Eğer NewValuekontrol edilir, sizin mantığında doğru koleksiyonu ile birlikte Geçerli öğeyi içerir:

    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)
        {
            ...
        }
    }

Başka bir örnek olarak, bu öğe işaretlendikten (kaldırıldıktan) sonra koleksiyonun boş olup olmayacağını belirlemek için:

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
her biri için ilk olarak, eğer koşul ise bir tane eklememiz gerekebilir ..if not item = checkedListBox1.Items[e.Index].ToString()
Lenin Raj Rajasekaran

8
Sorun, kontrol işlenmeden önce ItemCheck olayının tetiklenmesidir. Çözümünüz, temelde standart kodu çoğaltarak kendi listenizi tutmayı içerir. Duncan'ın ilk önerisi (ItemCheck'te gecikmeli yürütme), phq sorusuna en temiz cevap imo, çünkü herhangi bir ek işlem gerektirmiyor.
Berend Engelbrecht

35

Bununla ilgili çok sayıda StackOverflow yazısı var ... Branimir'in çözümünün yanı sıra , işte iki tane daha basit:

ItemCheck'te gecikmeli yürütme (ayrıca burada ):

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

MouseUp olayını kullanma :

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

İkinci seçenek yanlış pozitiflerle sonuçlanacağından (yani çok sık ateş etmek) ilk seçeneği tercih ediyorum.


13
İkinci yöntem, klavye aracılığıyla kontrol edilen veya kontrol edilmeyen öğeleri de gözden kaçırır.

1
BeginInvoke tam olarak ihtiyacım olan şeydi, çünkü etkinliğim aslında bir arayüze sesleniyordu, ne tür bir kontrolle uğraştığı hakkında hiçbir fikri yoktu. Kabul edilen yanıt yalnızca mantığın olay işleyicisinde veya doğrudan olay işleyiciden çağrılan bir şeyde gerçekleştirilebildiği durumlarda işe yarar. Benim için durum böyle değildi. Bu harika ama basit çözüm için teşekkürler.
Jesse

Thx, BeginInvoke ile ilk seçenek benim için çalışıyor. Bir konu bildirilen bu BUG 2010 yılında başlatıldığında neden Belki saçma bir açıklama millet .. ama değil 2018 yılında çözüldü ??
Goodies

1
@Goodies Kabul Edildi, ancak Microsoft'un şimdi davranışı değiştirmesi durumunda birçok kodu kırabileceğini tahmin ediyorum. Docs açıkça beyan The check state is not updated until after the ItemCheck event occurs. Farklı bir olay veya keyfi olmayan bir çözüm iyi bir IMO olacaktır.
Dunc

24

Bunu denedim ve işe yaradı:

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
Bu! ... maalesef en doğru cevap olmalı. Bu, işe yarayan saçma bir hack'tir çünkü M $ 'daki biri ItemCheckedolayı uygulamayı unuttu ve hiç kimse bunun var olmadığına değinmedi.
RLH

Tanım gereği bir hata olmasa da, bunun uygulanması gerektiğini düşünüyorum, eğer bu hata raporunu +1: connect.microsoft.com/VisualStudio/feedback/details/1759293
SCBuergel.eth

@Sebastian - burada düzeltme istemeyin. Bunun herhangi bir "düzeltmesi" mevcut çözümleri bozacaktır. İki olay olsaydı: ItemChecking, ItemChecked, o zaman ikinci tane daha içebilirim. Ancak yalnızca biri uygulanırsa ( ItemCheck), işleri doğru bir şekilde yapıyor demektir, yani olayı , değer kontrol edilmeden önce yeni değer ve parametre olarak sağlanan indeks ile tetiklemektir . "Değişiklik sonrası" olayını kim isterse, basitçe yukarıdakileri kullanabilir. Microsoft'a bir şey önerirseniz , mevcut olanı değiştirmeden yeni bir olay önerin ItemChecked: diimdeep'in cevabına bakın
miroxlav

Bunun gibi, ama her zaman kullandığım küçük bir alternatif, SetItemCheckState'in aynı olayı yeniden tetiklememesi için bir tür "atlama" bayrağı ayarlamaktır. Ya basit bir küresel ya da yapmaktan hoşlandığım şey, etiketinden emin olmaktır. örneğin, eylemi bir If myCheckListBox.Tag! = null içine sarın ve ardından Event Delete \ Add yerine, etiketi bir şeye (boş bir dizeye bile) ayarlayın ve ardından tekrar açmak için null değerine geri dönün.
da_jokker

10

Türetin CheckedListBoxve uygulayın

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

Bu BeginInvoke, ikinci cevaptaki çözümü kullanırken atlayabileceğiniz çok fazla ek kod .
Łukasƨ Fronczyk

4

İdeal olmasa da, ItemCheckolaya iletilen bağımsız değişkenleri kullanarak CheckedItems'i hesaplayabilirsiniz . MSDN'de bu örneğe bakarsanız , yeni değiştirilen öğenin işaretli olup olmadığını anlayabilirsiniz, bu da sizi öğelerle çalışmak için uygun bir konumda bırakır.

Hatta bir öğe kontrol edildikten sonra tetiklenen yeni bir etkinlik bile oluşturabilirsiniz, bu da size tam olarak istediğiniz şeyi verir.


1
Bu yeni olayın nasıl yaratılabileceği konusunda herhangi bir fikriniz var mı, CheckedItems öğelerinin ItemChecke olayından sonra güncellendiğini nasıl bilebilirim?
hultqvist

4

Bazı testlerden sonra, SelectedIndexChanged olayının ItemCheck olayından sonra tetiklendiğini görebiliyordum. CheckOnClick özelliğini True tutun

En iyi kodlama


Haklısın, bu en kolay yol. Ama yine de bilgisayar korsanlığı gibi bir şey, çünkü belgelenmemiş ve BEKLENMEYEN davranış. Microsoft'taki herhangi bir birinci sınıf öğrencisi şunu düşünebilir: peki, neden sadece Checkstate değiştiğinde SelectedIndexChanged'i ateşleyin. Bunu optimize edelim. Ve Bang kodunuza gidiyor :(
Rolf

Ayrıca, denetim durumunu programlı olarak değiştirdiğinizde SelectedIndexChanged etkinleşmez.
Rolf

1
Ve kontrol durumunu Space tuşu ile değiştirdiğinizde ateşlenmez. Bunu kullanmak yanlış.
Elmue

2

Bu işe yarıyor, ancak ne kadar zarif olduğundan emin değil!

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

Bunun geçerli olup olmadığını bilmiyorum ama sonuçları filtrelemek için bir onay listesi kutusu kullanmak istedim. Kullanıcı öğeleri işaretledikçe ve işaretlemedikçe, listenin öğeleri \ gizle göstermesini istedim.

Beni bu gönderiye götüren bazı sorunlar yaşıyordum. Özel bir şey olmadan nasıl yaptığımı paylaşmak istedim.

Not: CheckOnClick = true değerine sahibim ama muhtemelen olmadan da

Kullandığım olay " SelectedIndexChanged "

kullandığım numaralandırma " .CheckedItems "

Bu beklediğimizi düşündüğüm sonuçları veriyor. O kadar basitleştirildi ki ...

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

}

Kullanıcı kontrol durumunu Space tuşuyla değiştirdiğinde SelectedIndexChanged çalışmaz.
Elmue

Koddaki bir öğeyi kontrol etmek veya işaretini kaldırmak için SetItemChecked çağrıldığında SelectedIndexChanged çalışmıyor.
bkqc

1

Argümanları korumak ItemCheckancak model değiştirildikten sonra bildirim almak istediğinizi varsayarsak , model şöyle görünmelidir:

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

Nerede CheckedItemsChangedolabilir:

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

0

Bunu denedim ve işe yaradı:

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

listenin indeksine göre belirlenir.


-1

Bu sorunu çözmek için bir Zamanlayıcı kullanıyorum. Zamanlayıcıyı ItemCheck olayı aracılığıyla etkinleştirin. Timer's Tick olayında harekete geçin.

Bu, öğenin bir fare tıklamasıyla veya Boşluk Çubuğuna basarak kontrol edilip edilmediğini gösterir. Yeni kontrol edilen (veya kontrol edilmeyen) öğenin her zaman Seçili Öğe olması gerçeğinden yararlanacağız.

Zamanlayıcının Aralığı 1 kadar düşük olabilir. Tick olayı ortaya çıktığında, yeni Kontrol Edildi durumu ayarlanacaktır.

Bu VB.NET kodu kavramı gösterir. Kullanabileceğiniz birçok varyasyon var. Kullanıcının işlem yapmadan önce birkaç öğe üzerindeki kontrol durumunu değiştirmesine izin vermek için Zamanlayıcı Aralığını artırmak isteyebilirsiniz. Ardından, Tick olayında, Listedeki tüm Öğeler için sıralı geçiş yapın veya uygun eylemi gerçekleştirmek için CheckedItems koleksiyonunu kullanın.

Bu yüzden ilk olarak ItemCheck olayında Timer'ı devre dışı bırakıyoruz. Devre Dışı Bırak ve Etkinleştir, Aralık döneminin yeniden başlamasına neden olur.

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
Paylaştığın için teşekkürler. Öte yandan, belki başka cevaplardan daha iyi çözümler öğrenebilirsiniz. Zamanlayıcıyı kullanmak nispeten karmaşıktır ve bu durumda iş için yanlış bir araçtır, çünkü aslında zaten parametre olarak yeni değerler alıyorsunuz. Yani bu yanıtı tek seferlik çözüm için veya bunu sistematik çözüm için kullanabilirsiniz. Çevrimiçi dönüştürme araçlarından birini kullanarak bunları C #'dan VB'ye dönüştürün.
miroxlav

-1

Normal davranışta, bir öğeyi kontrol ettiğimizde, öğenin kontrol durumu, olay işleyicisi yükseltilmeden önce değişecektir. Ancak bir CheckListBox'ın farklı bir davranışı vardır: Olay işleyicisi, öğenin kontrol durumu değişmeden önce yükseltilir ve bu da işlerimizi düzeltmeyi zorlaştırır.

Bence bu sorunu çözmek için olay işleyicisini ertelemeliyiz.

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());}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.