Which CheckedListBox event triggers after a item is checked?

Viewed 103889

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 CheckedItems is updated it won't work out of the box.

What kind of method or event can I use to be notified when the CheckedItems is updated?

17 Answers

I tried this and it worked:

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

determine by index of the list.

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

Do you mean checkboxlist, rather than checkedlistbox? If so, the the event concerned would be SelectedIndexChanged.

e.g. handler definition head in VB:

Protected Sub cblStores_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cblStores.SelectedIndexChanged

I decided to just not care about the control's feelings, and handle the event as if the checkbox change indeed went through already. All you need to do is take the CheckedIndices list, and use the information in the ItemCheckEventArgs object to adjust it to the new state.

Then you can just loop over that list and retrieve the indicated items from the Items property of the control, and you have your CheckedItems list.

private void CheckedList_ItemCheck(Object sender, ItemCheckEventArgs e)
{
    CheckedListBox checkedList = sender as CheckedListBox;
    if (checkedList == null)
        return;
    // Somehow this still returns the state before the check, so update it manually.
    List<Int32> checkedIndices = checkedList.CheckedIndices.Cast<Int32>().ToList();
    if (e.NewValue == CheckState.Unchecked)
        checkedIndices.Remove(e.Index);
    else if (e.NewValue == CheckState.Checked)
        checkedIndices.Add(e.Index);
    checkedIndices.Sort()
    Int32 checkedItemCount = checkedIndices.Length;
    Object[] checkedItems = new Object[checkedItemCount]
    for (Int32 i = 0; i < checkedItemCount; i++)
        checkedItems[i] = checkedList.Items[checkedIndices[i]];
    this.UpdateAfterCheckChange(checkedItems);
}

The result is functionally identical to the hypothetical desired case where the event triggers only after the change.

VB.NET version of Dunc's answer to BeginInvoke a handler so the item is checked.

Private Sub ChkListBox1_ItemCheck(sender As Object, e As ItemCheckEventArgs) Handles ChkListBox1.ItemCheck


  Debug.WriteLine($"checked item count {ChkListBox1.CheckedItems.Count}")

  Debug.WriteLine($"{ChkListBox1.Items(e.Index)} - {e.Index} - {e.NewValue}")

  BeginInvoke(Sub() HandleItemCheck(e))

End Sub


Private Sub HandleItemCheck(e As ItemCheckEventArgs)

  Debug.WriteLine($"handle item {ChkListBox1.Items(e.Index)} - {e.Index} - {e.NewValue}")

  Debug.WriteLine($"checked item count handle item - {ChkListBox1.CheckedItems.Count}")

End Sub

If, like me, you were trying to use the Selection as one indicator (the item selected by the user), and the user wanting to changed the tick then I found a sneaky solution.

Form variables
    Private IsTicked As Boolean = False
    Private ListIndex = -1

Create a timer on the page. For instance, mine is called tmrBan, and I have a CheckBoxList called clbFTI.

Then, create a Click Event for your CheckBoxList.

Private Sub clbFTI_Click(sender As Object, e As EventArgs) Handles lbFTI.MouseClick
    ListIndex = sender.SelectedIndex
    IsTicked = clbFTI.SelectedIndices.Contains(ListIndex)
    tmrBan.Interval = 10
    tmrBan.Enabled = True
End Sub

Then, create a Tick Event for your timer

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles tmrBan.Tick
    clbFTI.SetItemChecked(ListIndex, IsTicked)
End Sub

You will see a flicker of the tick, but play around with the timer Interval to make this better for your case.

I ran into a similar issue: On a click of an item, the state should be converted from either checked/ non-checked to opposite. Here i post the event and the check and change:

CheckedListBox ChkLBox;
private void CheckedListBox_SelectedIndexChanged(object sender, EventArgs e)
{
    int SelectedIndex = ChkLBox.SelectedIndex; //
    var Item = ChkLBox.Items[SelectedIndex];

    bool IsChecked = (ChkLBox.GetItemChecked(ChkLBox.SelectedIndex));
    ChkLBox.SetItemChecked(ChkLBox.Items.IndexOf(Item), !IsChecked);
}
Related