Which CheckedListBox event triggers after a item i

2019-01-06 19:19发布

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?

11条回答
欢心
2楼-- · 2019-01-06 19:48

I use a Timer to solve this problem. Enable the timer via the ItemCheck event. Take action in the Timer's Tick event.

This works whether the item is checked via a mouse click or by pressing the Space-Bar. We'll take advantage of the fact that the item just checked (or un-checked) is always the Selected Item.

The Timer's Interval can be as low as 1. By the time the Tick event is raised, the new Checked status will be set.

This VB.NET code shows the concept. There are many variations you can employ. You may want to increase the Timer's Interval to allow the user to change the check status on several items before taking action. Then in the Tick event, make a sequential pass of all the Items in the List or use its CheckedItems collection to take appropriate action.

That's why we first disable the Timer in the ItemCheck event. Disable then Enable causes the Interval period to re-start.

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
查看更多
男人必须洒脱
3楼-- · 2019-01-06 19:55

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);
}
查看更多
一夜七次
4楼-- · 2019-01-06 19:57

You can use the ItemCheck event, if you also check the new state of the item which is being clicked. This is available in the event args, as e.NewValue. If NewValue is checked, include the current item along with the collection proper in your logic:

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

As another example, to determine if the collection will be empty after this item is (un-)checked:

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
        ...
}
查看更多
小情绪 Triste *
5楼-- · 2019-01-06 19:58

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

查看更多
Root(大扎)
6楼-- · 2019-01-06 19:59

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());}
查看更多
迷人小祖宗
7楼-- · 2019-01-06 20:03

After some tests, I could see that the event SelectedIndexChanged is triggered after the event ItemCheck. Keep the property CheckOnClick True

Best coding

查看更多
登录 后发表回答