Welches CheckedListBox-Ereignis wird ausgelöst, nachdem ein Element überprüft wurde?


93

Ich habe eine CheckedListBox, in der ich ein Ereignis möchte, nachdem ein Element überprüft wurde, damit ich CheckedItems mit dem neuen Status verwenden kann.

Da ItemChecked ausgelöst wird, bevor CheckedItems aktualisiert wird, funktioniert es nicht sofort.

Welche Art von Methode oder Ereignis kann ich verwenden, um benachrichtigt zu werden, wenn CheckedItems aktualisiert wird?

Antworten:


86

Sie können das ItemCheckEreignis verwenden, wenn Sie auch den neuen Status des Elements überprüfen, auf das geklickt wird. Dies ist in den Ereignisargumenten als verfügbar e.NewValue. Wenn diese NewValueOption aktiviert ist, fügen Sie das aktuelle Element zusammen mit der eigentlichen Sammlung in Ihre Logik ein:

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

Als weiteres Beispiel, um festzustellen, ob die Sammlung leer ist, nachdem dieses Element (nicht) aktiviert wurde:

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
in der ersten für jede müssen wir möglicherweise eine hinzufügen, wenn Bedingung ..if not item = checkedListBox1.Items[e.Index].ToString()
Lenin Raj Rajasekaran

8
Das Problem ist, dass das ItemCheck-Ereignis ausgelöst wird, bevor die Prüfung verarbeitet wird. Ihre Lösung würde darin bestehen, eine eigene Liste zu führen und im Wesentlichen den Standardcode zu duplizieren. Duncs erster Vorschlag (Verzögerte Ausführung auf ItemCheck) ist imo die sauberste Antwort auf die Frage von phq, da keine zusätzliche Behandlung erforderlich ist.
Berend Engelbrecht

34

Es gibt viele verwandte StackOverflow-Beiträge zu diesem Thema ... Neben der Lösung von Branimir gibt es hier zwei weitere einfache:

Verzögerte Ausführung bei ItemCheck (auch hier ):

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

Verwenden des MouseUp-Ereignisses :

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

Ich bevorzuge die erste Option, da die zweite zu Fehlalarmen führen würde (dh zu oft feuern).


13
Bei der zweiten Methode würden auch Elemente übersehen, die über die Tastatur aktiviert oder deaktiviert werden.

1
BeginInvoke war genau das, was ich brauchte, da meine Veranstaltung tatsächlich eine Schnittstelle anrief, die keine Ahnung hatte, um welche Art von Steuerung es sich handelte. Die akzeptierte Antwort funktioniert nur in Fällen, in denen die Logik innerhalb des Ereignishandlers oder direkt vom Ereignishandler aufgerufen werden kann. Dies war bei mir nicht der Fall. Vielen Dank für diese großartige und dennoch einfache Lösung.
Jesse

Danke, die erste Option mit BeginInvoke funktioniert bei mir. Vielleicht ein dummer Kommentar Leute .. aber warum wird dieser BUG in einem 2010 gestarteten Thema gemeldet, das 2018 nicht gelöst wurde?
Goodies

1
@ Goodies Einverstanden, obwohl ich denke, es könnte eine Menge Code brechen, wenn Microsoft das Verhalten jetzt ändert. In den Dokumenten wird explizit angegeben The check state is not updated until after the ItemCheck event occurs. Ein anderes Ereignis oder eine nicht willkürliche Problemumgehung wäre nett, IMO.
Dunc

24

Ich habe es versucht und es hat funktioniert:

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
Dies! ... sollte die richtige Antwort sein, was leider am meisten ist. Dies ist ein lächerlicher Hack, der funktioniert, weil jemand bei M $ vergessen hat, das ItemCheckedEreignis zu implementieren , und niemand jemals angesprochen hat, dass es nicht existiert.
RLH

Obwohl es per Definition kein Fehler ist, denke ich, dass dies implementiert werden sollte, sollten Sie diesen Fehlerbericht unterstützen, indem Sie auf +1 klicken: connect.microsoft.com/VisualStudio/feedback/details/1759293
SCBuergel.eth

@Sebastian - bitte hier nicht um Korrektur. Jede "Lösung" würde bestehende Lösungen zerstören. Wenn es zwei Ereignisse gäbe : ItemChecking, ItemCheckedkönnten Sie das letztere verwenden. Wenn jedoch nur eines implementiert ist ( ItemCheck), werden die Dinge korrekt ausgeführt, dh das Ereignis wird ausgelöst, bevor der Wert mit einem neuen Wert und einem neuen Index als Parameter überprüft wird. Wer das "After Change" -Ereignis möchte, kann einfach das oben genannte verwenden. Wenn Sie Microsoft etwas vorschlagen, dann schlagen Sie ein neues Ereignis vor ItemChecked , ohne das bestehende zu ändern: siehe die Antwort von
diimdeep

So, aber eine kleine Alternative, die ich ständig benutze, besteht darin, eine Art "Überspringen" -Flag zu setzen, damit der SetItemCheckState nicht dasselbe Ereignis erneut auslöst. Entweder eine einfache globale oder ich möchte das Tag sicherstellen. Wickeln Sie die Aktion beispielsweise in If MyCheckListBox.Tag! = null ein und setzen Sie das Tag anstelle von Event Delete \ Add einfach auf etwas (sogar eine leere Zeichenfolge) und dann wieder auf null, um es wieder einzuschalten.
da_jokker

10

Ableiten von CheckedListBoxund implementieren

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

4

Obwohl dies nicht ideal ist, können Sie die CheckedItems mithilfe der Argumente berechnen, die an das ItemCheckEreignis übergeben werden. Wenn Sie sich dieses Beispiel auf MSDN ansehen , können Sie herausfinden, ob das neu geänderte Element aktiviert oder deaktiviert wurde, sodass Sie in einer geeigneten Position sind, um mit den Elementen zu arbeiten.

Sie können sogar ein neues Ereignis erstellen, das ausgelöst wird, nachdem ein Element überprüft wurde. Dadurch erhalten Sie genau das, was Sie möchten, wenn Sie dies wünschen.


1
Haben Sie eine genaue Vorstellung davon, wie dieses neue Ereignis erstellt werden kann? Wie kann ich feststellen, wann CheckedItems nach dem ItemChecke-Ereignis aktualisiert wurden?
Hultqvist

4

Nach einigen Tests konnte ich feststellen, dass das Ereignis SelectedIndexChanged nach dem Ereignis ItemCheck ausgelöst wird. Behalten Sie die Eigenschaft CheckOnClick True bei

Beste Codierung


Du hast recht, das ist der einfachste Weg. Aber es ist immer noch so etwas wie ein Hack, weil es undokumentiertes und unerwartetes Verhalten ist. Jeder Neuling bei Microsoft könnte denken: Na ja, warum SelectedIndexChanged auslösen, wenn sich nur der Checkstate ändert. Lassen Sie uns das optimieren. Und Bang geht dein Code :(
Rolf

Darüber hinaus wird SelectedIndexChanged nicht ausgelöst, wenn Sie den Prüfstatus programmgesteuert ändern.
Rolf

1
Und es wird nicht ausgelöst, wenn Sie den Prüfstatus mit der Leertaste ändern. Es ist falsch, dies zu verwenden.
Elmue

2

Dies funktioniert, nicht sicher, wie elegant es ist!

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

Ich weiß nicht, ob dies zutrifft, aber ich wollte ein Kontrollkästchen verwenden, um die Ergebnisse zu filtern. Da der Benutzer Elemente aktiviert und deaktiviert hat, sollte die Liste Elemente anzeigen / ausblenden.

Ich hatte einige Probleme, die mich zu diesem Beitrag führten. Ich wollte nur erzählen, wie ich es ohne etwas Besonderes gemacht habe.

Hinweis: Ich habe CheckOnClick = true, aber ohne würde es wahrscheinlich immer noch funktionieren

Das von mir verwendete Ereignis ist " SelectedIndexChanged ".

Die von mir verwendete Aufzählung lautet " .CheckedItems ".

Dies gibt die Ergebnisse, von denen ich denke, dass wir sie erwarten können. So vereinfacht kommt es darauf an ...

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 wird nicht ausgelöst, wenn der Benutzer den Prüfstatus mit der Leertaste ändert.
Elmue

SelectedIndexChanged wird nicht ausgelöst, wenn SetItemChecked aufgerufen wird, um ein Element im Code zu aktivieren oder zu deaktivieren.
bkqc

1

Angenommen, Sie möchten die Argumente beibehalten, werden ItemCheckaber benachrichtigt, nachdem das Modell geändert wurde, sollte dies folgendermaßen aussehen:

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

Wo CheckedItemsChangedkönnte sein:

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

0

Ich benutze einen Timer, um dieses Problem zu lösen. Aktivieren Sie den Timer über das ItemCheck-Ereignis. Ergreifen Sie Maßnahmen beim Timer's Tick-Event.

Dies funktioniert unabhängig davon, ob das Element per Mausklick oder durch Drücken der Leertaste überprüft wird. Wir werden die Tatsache ausnutzen, dass der gerade aktivierte (oder nicht aktivierte) Artikel immer der ausgewählte Artikel ist.

Das Intervall des Timers kann so niedrig wie 1 sein. Wenn das Tick-Ereignis ausgelöst wird, wird der neue Status "Überprüft" gesetzt.

Dieser VB.NET-Code zeigt das Konzept. Es gibt viele Variationen, die Sie verwenden können. Möglicherweise möchten Sie das Intervall des Timers erhöhen, damit der Benutzer den Prüfstatus für mehrere Elemente ändern kann, bevor er Maßnahmen ergreift. Führen Sie dann im Tick-Ereignis einen sequentiellen Durchlauf aller Elemente in der Liste durch, oder verwenden Sie die CheckedItems-Auflistung, um entsprechende Maßnahmen zu ergreifen.

Aus diesem Grund deaktivieren wir zuerst den Timer im ItemCheck-Ereignis. Deaktivieren und dann aktivieren bewirkt, dass die Intervallperiode neu gestartet wird.

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

Danke für das Teilen. Auf der anderen Seite können Sie vielleicht aus anderen Antworten bessere Lösungen lernen. Die Verwendung des Timers ist relativ kompliziert und in diesem Fall ein falsches Werkzeug für den Job, da Sie tatsächlich bereits neue Werte als Parameter erhalten. So können Sie entweder verwenden Sie diese Antwort für einmalige Lösung oder diese für eine systematische Lösung. Konvertieren Sie sie mit einem der Online-Konvertierungstools von C # in VB.
Miroxlav

0

Wenn wir bei normalem Verhalten ein Element überprüfen, ändert sich der Überprüfungsstatus des Elements, bevor der Ereignishandler ausgelöst wird. Eine CheckListBox hat jedoch ein anderes Verhalten: Der Ereignishandler wird ausgelöst, bevor sich der Prüfstatus des Elements ändert, und dies macht es schwierig, unsere Jobs zu korrigieren.

Um dieses Problem zu lösen, sollten wir meiner Meinung nach den Event-Handler verschieben.

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

0

Ich habe es versucht und es hat funktioniert:

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

Bestimmen Sie anhand des Index der Liste.

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.