J'ai un CheckedListBox où je veux un événement après un élément est vérifié afin que je puisse utiliser CheckedItems avec le nouvel état.
Etant donné que 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 les CheckedItems sont mis à jour?
Vous pouvez utiliser l'événement ItemCheck
si vous vérifiez également le nouvel état de l'élément sur lequel vous cliquez. Ceci est disponible dans les arguments de l'événement, sous la forme e.NewValue
. Si NewValue
est coché, incluez l'élément actuel avec la collection proprement dite 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)
{
...
}
}
Autre exemple, pour déterminer si la collection sera vide après que cet élément soit (dés) 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
...
}
Il y a beaucoup de publications StackOverflow sur ce sujet ... En plus de Branimir's solution, voici deux autres plus simples:
Exécution différée sur ItemCheck (aussi 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 deuxième entraînerait des faux positifs (c’est-à-dire qu’elle se déclencherait trop souvent).
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();
}
Dérivez de CheckedListBox
et implémentez
/// <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);
}
Bien que cela ne soit pas idéal, vous pouvez calculer les CheckedItems en utilisant les arguments transmis à l'événement ItemCheck
. Si vous consultez cet exemple sur MSDN , vous pouvez déterminer si le nouvel élément modifié a été coché ou non, ce qui vous laisse dans une position appropriée pour utiliser 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.
Après quelques tests, j'ai pu constater que l'événement SelectedIndexChanged était déclenché après l'événement ItemCheck. Conservez la propriété CheckOnClick True
Meilleur codage
Cela fonctionne, pas sûr de son élégance!
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
En supposant que vous souhaitiez conserver les arguments de ItemCheck
mais que vous soyez averti après la modification du modèle, il devrait ressembler à cela:
CheckedListBox ctrl = new CheckedListBox();
ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e)));
Où CheckedItemsChanged
pourrait être:
private void CheckedItemsChanged(object sender, EventArgs e)
{
DoYourThing();
}
Je ne sais pas si cela s'applique, mais je voulais utiliser une liste de contrôle pour filtrer les résultats. Ainsi, en tant qu'utilisateur coché et décoché des éléments, je voulais que la liste affiche\masquer les éléments.
J'avais des problèmes qui m'ont amené à ce poste. Je voulais juste partager comment je l'ai fait sans rien de spécial.
Note: J'ai CheckOnClick = true mais cela fonctionnerait probablement encore sans
L'événement que j'utilise est " SelectedIndexChanged "
l'énumération que j'utilise est " .CheckedItems "
Cela donne les résultats que je pense pouvoir espérer. 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
}
}
}
En comportement normal, lorsque nous vérifions un élément, l'état de contrôle de l'élément change avant que le gestionnaire d'événements ne soit déclenché . Mais un contrôle CheckListBox a un comportement différent: il est difficile de corriger nos emplois.
À 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());}
J'utilise un minuteur pour résoudre ce problème. Activer la minuterie via l'événement ItemCheck. Agissez lors de l'événement Tick du minuteur.
Cela fonctionne que l'élément soit vérifié via un clic de souris ou en appuyant sur la barre d'espacement. Nous tirerons parti du fait que l'élément qui vient d'être coché (ou non coché) est toujours l'élément sélectionné.
L'intervalle du minuteur peut être aussi bas que 1. Au moment où l'événement Tick est déclenché, le nouvel état Checked sera défini.
Ce code VB.NET montre le concept. Vous pouvez utiliser de nombreuses variantes. Vous souhaiterez peut-être augmenter l'intervalle du minuteur pour permettre à l'utilisateur de modifier l'état de la vérification de plusieurs éléments avant de prendre des mesures. Ensuite, dans l'événement Tick, effectuez une passe séquentielle 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 provoque 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