I have a CheckedListBox where I want an event after an item is checked so that I can use CheckedItems with the new state.
Since ItemChecked is fired before
Assuming you want to preserve the arguments from ItemCheck
but get notified after the model was changed it should look like that:
CheckedListBox ctrl = new CheckedListBox();
ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e)));
Where CheckedItemsChanged
could be:
private void CheckedItemsChanged(object sender, EventArgs e)
{
DoYourThing();
}
In normal behaviour, when we check one item, the item's check state will change before the event handler is raised. But a CheckListBox has a different behaviour: The event handler is raised before the check state of the item changes and that makes it difficult to correct our jobs.
In my opinion, to solve this problem, we should defer the event handler.
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());}
Don't know if this applies but I wanted to use a checklistbox to filter results. So as the user checked and unchecked items I wanted the list to show\hide items.
I was having some issues which led me to this post. Just wanted to share how I did it without anything special.
Note: I have CheckOnClick = true but it would probably still work without
The event I use is "SelectedIndexChanged"
the enumeration I use is ".CheckedItems"
This give the results I think we may expect. So simplified it comes down to ....
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
}
}
}
Derive from CheckedListBox
and implement
/// <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);
}
I tried this and it worked:
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();
}
There are lots of related StackOverflow posts on this... As well as Branimir's solution, here are two more simple ones:
Delayed execution on ItemCheck (also here):
void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
this.BeginInvoke((MethodInvoker) (
() => Console.WriteLine(checkedListBox1.SelectedItems.Count)));
}
Using the MouseUp event:
void checkedListBox1_MouseUp(object sender, MouseEventArgs e)
{
Console.WriteLine(checkedListBox1.SelectedItems.Count);
}
I prefer the first option, as the second would result in false positives (i.e. firing too often).