Quel événement CheckedListBox se déclenche après la vérification d'un élément?

96

J'ai un CheckedListBox où je veux un événement après qu'un élément est vérifié afin que je puisse utiliser CheckedItems avec le nouvel état.

Étant donné qu'ItemChecked est déclenché avant la mise à jour de CheckedItems, cela ne fonctionnera pas immédiatement.

Quel type de méthode ou d'événement puis-je utiliser pour être averti lorsque CheckedItems est mis à jour?

hultqvist
la source

Réponses:

88

Vous pouvez utiliser l' ItemCheckévénement si vous vérifiez également le nouvel état de l'élément sur lequel l'utilisateur clique. Ceci est disponible dans l'événement args, comme e.NewValue. Si NewValueest coché, incluez l'élément actuel avec la collection appropriée dans votre logique:

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

Comme autre exemple, pour déterminer si la collection sera vide une fois que cet élément est (non) vérifié:

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
        ...
}
Branimir
la source
3
dans le premier pour chacun, nous pourrions avoir besoin d'en ajouter un si condition ..if not item = checkedListBox1.Items[e.Index].ToString()
Lenin Raj Rajasekaran
8
Le problème est que l'événement ItemCheck est déclenché avant le traitement de la vérification. Votre solution consisterait à conserver votre propre liste, en dupliquant essentiellement le code standard. La première suggestion de Dunc (Exécution retardée sur ItemCheck) est imo la réponse la plus claire à la question de phq, car elle ne nécessite aucune manipulation supplémentaire.
Berend Engelbrecht
34

Il y a beaucoup d'articles liés à StackOverflow à ce sujet ... En plus de la solution de Branimir , en voici deux plus simples:

Exécution retardée sur ItemCheck (également ici ):

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

Utilisation de l'événement MouseUp :

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

Je préfère la première option, car la seconde entraînerait des faux positifs (c'est-à-dire tirant trop souvent).

Dunc
la source
13
La deuxième méthode manquerait également les éléments vérifiés ou décochés via le clavier.
1
BeginInvoke était exactement ce dont j'avais besoin car mon événement appelait en fait une interface, qui n'avait aucune idée de quel type de contrôle il traitait. La réponse acceptée ne fonctionne que dans les cas où la logique peut être exécutée dans le gestionnaire d'événements, ou quelque chose appelé directement à partir du gestionnaire d'événements. Ce n'était pas le cas pour moi. Merci pour cette solution géniale mais simple.
Jesse
Thx, la première option avec BeginInvoke fonctionne pour moi. Peut-être un commentaire idiot, mais pourquoi ce BUG est-il signalé dans un sujet commencé en 2010 et non résolu en 2018 ??
Goodies
1
@ Goodies D'accord, même si je suppose que cela pourrait casser beaucoup de code si Microsoft changeait le comportement maintenant. Les documents indiquent explicitement The check state is not updated until after the ItemCheck event occurs. Un événement différent ou une solution de contournement non arbitraire serait une bonne OMI.
Dunc
24

Je l'ai essayé et ça marche:

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();        
}
softburger
la source
8
Ce! ... devrait être la bonne réponse, ce qui est le plus malheureusement. C'est un hack ridicule qui fonctionne parce que quelqu'un chez M $ a oublié d'implémenter l' ItemCheckedévénement, et personne n'a jamais répondu qu'il n'existait pas.
RLH du
Bien que ce ne soit pas par définition un bogue, je pense que cela devrait être implémenté, si vous acceptez, envisagez de prendre en charge ce rapport de bogue en cliquant sur +1: connect.microsoft.com/VisualStudio/feedback/details/1759293
SCBuergel.eth
@Sebastian - ne demandez pas de solution ici. Toute «solution» à ce problème briserait les solutions existantes. S'il y avait deux événements: ItemChecking, ItemChecked, vous pouvez alors utiliser celui- ci un. Mais si un seul est implémenté ( ItemCheck), il fait les choses correctement, c'est-à-dire déclenche l'événement avant que la valeur ne soit vérifiée avec une nouvelle valeur et un index fournis en paramètres. Quiconque souhaite l'événement «après le changement» peut simplement utiliser ce qui précède. Si vous suggérez quelque chose à Microsoft, suggérez un nouvel événement ItemChecked , sans changer celui existant: voir la réponse de
diimdeep
Comme ça, mais une légère alternative que j'utilise tout le temps est juste de définir une sorte d'indicateur "ignorer" afin que SetItemCheckState ne re-déclenche pas le même événement. Soit un simple global, soit ce que j'aime faire, c'est m'assurer de la balise. par exemple, encapsulez l'action dans If myCheckListBox.Tag! = null, puis à la place de Event Delete \ Add, définissez simplement la balise sur quelque chose (même une chaîne vide), puis revenez à null pour la réactiver.
da_jokker
10

Dériver CheckedListBoxet mettre en œuvre

/// <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);
}
diimdeep
la source
4

Bien que ce ne soit pas idéal, vous pouvez calculer les CheckedItems à l'aide des arguments transmis à l' ItemCheckévénement. Si vous regardez cet exemple sur MSDN , vous pouvez déterminer si l'élément nouvellement modifié a été coché ou non, ce qui vous laisse dans une position appropriée pour travailler avec les éléments.

Vous pouvez même créer un nouvel événement qui se déclenche après la vérification d'un élément, ce qui vous donnerait exactement ce que vous vouliez si vous le souhaitiez.

Iain Ward
la source
1
Avez-vous une idée précise de la façon dont ce nouvel événement pourrait être créé, comment puis-je savoir quand CheckedItems a été mis à jour après l'événement ItemChecke?
hultqvist
4

Après quelques tests, j'ai pu voir que l'événement SelectedIndexChanged est déclenché après l'événement ItemCheck. Conservez la propriété CheckOnClick True

Meilleur codage

Antonio Leite
la source
Vous avez raison, c'est le moyen le plus simple. Mais c'est toujours quelque chose comme un hack, car c'est un comportement non documenté et imprévu. Tout étudiant de première année chez Microsoft pourrait penser: eh bien, pourquoi déclencher SelectedIndexChanged lorsque seul le Checkstate change. Optimisons cela. Et Bang passe votre code :(
Rolf
En outre, SelectedIndexChanged ne se déclenche pas lorsque vous modifiez l'état de vérification par programme.
Rolf
1
Et il ne se déclenche pas lorsque vous modifiez l'état de vérification avec la touche Espace. Il est faux de l'utiliser.
Elmue
2

Cela fonctionne, je ne sais pas à quel point c'est élégant!

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
FireMatt
la source
1

Je ne sais pas si cela s'applique, mais je voulais utiliser une liste de contrôle pour filtrer les résultats. Donc, comme l'utilisateur a vérifié et décoché les éléments, je voulais que la liste affiche \ masquer les éléments.

J'avais des problèmes qui m'ont conduit à ce post. Je voulais juste partager comment je l'ai fait sans rien de spécial.

Remarque: j'ai CheckOnClick = true mais cela fonctionnerait probablement toujours sans

L'événement que j'utilise est " SelectedIndexChanged "

l'énumération que j'utilise est " .CheckedItems "

Cela donne les résultats auxquels je pense que nous pouvons nous attendre. Si simplifié, cela revient à ...

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

}
da_jokker
la source
SelectedIndexChanged ne se déclenche pas lorsque l'utilisateur modifie l'état de vérification avec la touche Espace.
Elmue
SelectedIndexChanged ne se déclenche pas lorsque vous appelez SetItemChecked pour cocher ou décocher un élément dans le code.
bkqc
1

En supposant que vous souhaitiez conserver les arguments de ItemCheckmais être notifié après la modification du modèle, cela devrait ressembler à ceci:

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

CheckedItemsChangedpourrait être:

private void CheckedItemsChanged(object sender, EventArgs e)
{
    DoYourThing();
}
Slion
la source
0

J'utilise une minuterie pour résoudre ce problème. Activez le minuteur via l'événement ItemCheck. Agissez dans l'événement Timer's Tick.

Cela fonctionne que l'élément soit vérifié via un clic de souris ou en appuyant sur la barre d'espace. Nous profiterons du fait que l'élément qui vient d'être coché (ou non coché) est toujours l'élément sélectionné.

L'intervalle de la minuterie peut être aussi bas que 1. Au moment où l'événement Tick est déclenché, le nouvel état Vérifié sera défini.

Ce code VB.NET montre le concept. Il existe de nombreuses variantes que vous pouvez utiliser. Vous souhaiterez peut-être augmenter l'intervalle du minuteur pour permettre à l'utilisateur de modifier l'état de vérification de plusieurs éléments avant d'agir. Ensuite, dans l'événement Tick, effectuez un passage séquentiel de tous les éléments de la liste ou utilisez sa collection CheckedItems pour prendre les mesures appropriées.

C'est pourquoi nous désactivons d'abord le minuteur dans l'événement ItemCheck. Désactiver puis Activer entraîne le redémarrage de la période d'intervalle.

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
Bob Ashcraft
la source
Merci pour le partage. D'autre part, vous pouvez peut-être apprendre de meilleures solutions à partir d'autres réponses. L'utilisation de la minuterie est relativement compliquée et dans ce cas, c'est un mauvais outil pour le travail, car vous obtenez déjà de nouvelles valeurs en tant que paramètres. Vous pouvez donc utiliser cette réponse pour une solution ponctuelle ou celle-ci pour une solution systématique. Convertissez-les de C # en VB en utilisant l'un des outils de conversion en ligne.
miroxlav
0

Dans un comportement normal, lorsque nous vérifions un élément, l'état de vérification de l'élément changera avant que le gestionnaire d'événements ne soit déclenché. Mais un CheckListBox a un comportement différent: le gestionnaire d'événements est déclenché avant que l'état de vérification de l'élément change et cela rend difficile la correction de nos travaux.

À mon avis, pour résoudre ce problème, nous devrions différer le gestionnaire d'événements.

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());}
Thinh Vu
la source
0

Je l'ai essayé et ça marche:

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

déterminer par index de la liste.

Seon Hyun KIM
la source