检查项目后触发哪个CheckedListBox事件?


96

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

由于在CheckedItems更新之前就触发了ItemChecked,因此无法立即使用。

更新CheckedItems时可以使用哪种方法或事件进行通知?

Answers:


88

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

3
第一对每个,我们可能需要添加一个,如果条件..if not item = checkedListBox1.Items[e.Index].ToString()
列宁拉吉Rajasekaran

8
问题在于处理检查之前会触发ItemCheck事件。您的解决方案将包括保留自己的列表,实质上是复制标准代码。Dunc的第一个建议(在ItemCheck上延迟执行)是imo对phq问题的最干净的答案,因为它不需要任何其他处理。
Berend Engelbrecht 2014年

34

关于此,有很多相关的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);
    }

我倾向于第一种选择,因为第二种选择会导致误报(即触发次数过多)。


13
第二种方法还会丢失通过键盘检查或取消检查的项目。

1
BeginInvoke正是我需要的,因为我的事件实际上正在调用接口,但不知道它正在处理哪种控件。可接受的答案仅在逻辑可以在事件处理程序中执行或直接从事件处理程序调用的情况下有效。对我而言并非如此。感谢您提供这个很棒而又简单的解决方案。
杰西

谢谢,BeginInvoke的首选对我有用。也许一个愚蠢的评论伙计..但是为什么这个BUG在2010年开始的一个话题中报道,到2018 年仍未解决?
东西

1
@Goodies同意,尽管我猜想如果微软现在改变行为,它可能会破坏很多代码。该文件明确规定The check state is not updated until after the ItemCheck event occurs。一个不同的事件或非任意的解决方法将是不错的IMO。
Dunc

24

我尝试了一下,它起作用了:

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

8
这个!...应该是正确的答案,最不幸的是。这是一个荒谬的骇客,之所以奏效,是因为M $的某人忘记了实施该ItemChecked活动,而且从未有人提出过该活动不存在。
RLH 2014年

尽管按照定义,这不是错误,但我认为应该执行此错误,如果您同意考虑通过单击+1来支持此错误报告:connect.microsoft.com/VisualStudio/feedback/details/1759293
SCBuergel.eth

@Sebastian –在此不要求修复。任何“解决办法”都将破坏现有的解决方案。如果有两个事件:ItemChecking,,ItemChecked则可以使用后一个。但是,如果仅实现一个(ItemCheck),则它可以正确地执行操作,即使用新值和作为参数提供的索引检查值之前触发事件。谁想要“更改后”事件,他们都可以简单地使用以上内容。如果向微软提出建议,则建议提出一个新事件 ItemChecked,而不是改变现有事件:请参阅diimdeep的答案
miroxlav

像这样,但我一直使用的一个轻微替代方法只是设置某种“跳过”标志,以使SetItemCheckState不会重新触发同一事件。一个简单的全局变量,或者我想做的就是确保标记。例如,将操作包装在If myCheckListBox.Tag!= null中,然后代替事件Delete \ Add,只需将标签设置为某种值(甚至是一个空字符串),然后再将其设置为null即可将其重新打开。
da_jokker

10

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

4

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

您甚至可以创建一个新事件,在检查项目后将其触发,这将为您提供所需的确切信息。


1
您对如何创建此新事件有任何特定的想法,如何知道在ItemChecke事件之后何时更新CheckedItems?
hultqvist 2010年

4

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

最佳编码


没错,这是最简单的方法。但这仍然像骇客一样,因为它是无证的和意外的行为。微软的任何新生可能会想到:哦,当只更改Checkstate时,为什么要触发SelectedIndexChanged。让我们对其进行优化。Bang会输入您的代码:(
Rolf

此外,以编程方式更改检查状态时,不会触发SelectedIndexChanged。
罗尔夫(Rolf)2015年

1
当您使用空格键更改检查状态时,它不会触发。使用它是错误的。
Elmue

2

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

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

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

}

当用户使用空格键更改检查状态时,不会触发SelectedIndexChanged。
Elmue

调用SetItemChecked检查或取消选中代码中的项目时,不会触发SelectedIndexChanged。
bkqc

1

假设您想保留其中的参数,ItemCheck但是在更改模型后得到通知,则它应如下所示:

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

CheckedItemsChanged可能在哪里:

private void CheckedItemsChanged(object sender, EventArgs e)
{
    DoYourThing();
}

0

我使用计时器来解决此问题。通过ItemCheck事件启用计时器。在“计时器的滴答”事件中采取措施。

无论通过鼠标单击还是按下空格键来检查项目,此方法均有效。我们将利用这样一个事实,即刚刚选中(或未选中)的项目始终是Selected Item。

计时器的间隔可以低至1。在Tick事件引发时,将设置新的Checked状态。

此VB.NET代码说明了这一概念。您可以采用多种变体。您可能希望增加计时器的间隔,以允许用户在执行操作之前更改多个项目的检查状态。然后在Tick事件中,顺序传递列表中的所有项目,或使用其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

谢谢你的分享。另一方面,也许您可​​以从其他答案中学到更好的解决方案。使用Timer相对复杂,在这种情况下,它是工作的错误工具,因为实际上您已经在获取新值作为参数。因此,您可以将这一答案用于一次性解决方案,也可以将其用于系统解决方案。使用在线转换工具之一将它们从C#转换为VB。
miroxlav

0

在正常情况下,当我们检查一项时,在引发事件处理程序之前,该项目的检查状态将更改。但是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());}

0

我尝试了一下,它起作用了:

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

通过列表的索引确定。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.