106

我有一个 CheckedListBox,在检查项目我想要一个事件,以便我可以将 CheckedItems 与新状态一起使用。

由于 ItemChecked 在 CheckedItems 更新之前被触发,因此它不会开箱即用。

当 CheckedItems 更新时,我可以使用哪种方法或事件来获得通知?

4

16 回答 16

97

ItemCheck如果您还检查正在单击的项目的新状态,则可以使用该事件。这在事件 args 中可用,如e.NewValue. 如果NewValue选中,则在您的逻辑中包含当前项目以及适当的集合:

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

作为另一个示例,要确定在(取消)选中此项后集合是否为空:

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
        ...
}
于 2010-09-08T11:00:22.323 回答
44

关于这个有很多相关的 StackOverflow 帖子......以及Branimir 的解决方案,这里有两个更简单的:

ItemCheck 的延迟执行(也在这里):

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

使用 MouseUp 事件

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

我更喜欢第一个选项,因为第二个会导致误报(即过于频繁地触发)。

于 2013-06-05T09:43:24.580 回答
26

我试过了,它奏效了:

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();        
}
于 2013-07-07T11:26:41.483 回答
11

派生CheckedListBox和实施

/// <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);
}
于 2013-08-28T12:11:49.403 回答
4

尽管不理想,但您可以使用传递给ItemCheck事件的参数来计算 CheckedItems。如果您查看MSDN 上的此示例,您可以确定新更改的项目是已选中还是未选中,这使您处于处理这些项目的合适位置。

您甚至可以创建一个在检查项目后触发的新事件,如果您愿意,它会为您提供您想要的东西。

于 2010-09-08T11:01:47.980 回答
4

经过一些测试,我可以看到 SelectedIndexChanged 事件是在 ItemCheck 事件之后触发的。保持属性 CheckOnClick True

最佳编码

于 2014-08-11T19:02:19.737 回答
3

这行得通,但不确定它有多优雅!

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
于 2013-06-10T19:35:29.143 回答
1

不知道这是否适用,但我想使用复选框来过滤结果。因此,当用户选中和未选中的项目时,我希望列表显示\隐藏项目。

我遇到了一些问题,导致我写了这篇文章。只是想分享我是如何做到的,没有什么特别的。

注意:我有CheckOnClick = true但如果没有它可能仍然可以工作

我使用的事件是“ SelectedIndexChanged

我使用的枚举是“ .CheckedItems

这给出了我认为我们可能期望的结果。如此简化归结为....

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

}
于 2015-08-14T17:26:24.043 回答
1

我试过了,它奏效了:

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

通过列表的索引确定。

于 2020-06-26T09:10:26.707 回答
1

假设您想保留参数ItemCheck但在模型更改后收到通知,它应该如下所示:

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

CheckedItemsChanged可能在哪里:

private void CheckedItemsChanged(object sender, EventArgs e)
{
    DoYourThing();
}
于 2018-02-06T14:40:00.940 回答
0

如果像我一样,您试图将选择用作一个指标(用户选择的项目),并且用户想要更改刻度,那么我找到了一个偷偷摸摸的解决方案。

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

在页面上创建一个计时器。例如,我的被称为tmrBan,而我有一个CheckBoxList被称为clbFTI

然后,为您的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

然后,为您的计时器创建一个 Tick 事件

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

您会看到滴答声的闪烁,但请使用计时器 Interval 以使其更适合您的情况。

于 2021-08-01T13:47:55.700 回答
0

你的意思是checkboxlist,而不是checkedlistbox?如果是这样,则相关事件将是 SelectedIndexChanged。

例如 VB 中的处理程序定义头:

Protected Sub cblStores_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cblStores.SelectedIndexChanged
于 2020-12-01T11:45:47.007 回答
0

我决定不关心控件的感受,并处理该事件,就好像复选框更改确实已经完成一样。您需要做的就是获取CheckedIndices列表,并使用ItemCheckEventArgs对象中的信息将其调整为新状态。

然后,您可以遍历该列表并从Items控件的属性中检索指示的项目,并且您有您的CheckedItems列表。

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

结果在功能上与仅在更改后才触发事件的假设期望情况相同。

于 2021-02-25T17:54:32.623 回答
0

VB.NET 版本的DuncBeginInvoke处理程序的回答,以便检查项目。

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
于 2021-06-24T16:27:58.600 回答
-1

我使用定时器来解决这个问题。通过 ItemCheck 事件启用计时器。在 Timer's Tick 事件中采取行动。

无论是通过鼠标单击还是按空格键检查项目,这都有效。我们将利用刚刚选中(或未选中)的项目始终是选定项目这一事实。

Timer 的 Interval 可以低至 1。在引发 Tick 事件时,将设置新的 Checked 状态。

这个 VB.NET 代码显示了这个概念。您可以使用许多变体。您可能希望增加计时器的时间间隔,以允许用户在采取行动之前更改多个项目的检查状态。然后在 Tick 事件中,依次传递 List 中的所有项目或使用其 CheckedItems 集合来采取适当的操作。

这就是我们首先在 ItemCheck 事件中禁用 Timer 的原因。禁用然后启用会导致间隔周期重新开始。

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
于 2015-04-10T20:29:51.187 回答
-1

在正常行为中,当我们检查一个项目时,该项目的检查状态将在事件处理程序被引发之前改变。但是 CheckListBox 有不同的行为:事件处理程序在项目的检查状态更改之前引发,这使得我们很难纠正我们的工作。

在我看来,要解决这个问题,我们应该推迟事件处理程序。

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());}
于 2015-11-17T05:32:11.443 回答