Which CheckedListBox event triggers after a item is checked?
我有一个CheckedListBox,我想在检查项目后在其中想要一个事件,以便可以将CheckedItems与新状态一起使用。
由于在CheckedItems更新之前已触发ItemChecked,因此无法立即使用。
更新CheckedItems时,可以使用哪种方法或事件来通知?
如果还检查正在单击的项目的新状态,则可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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) { ... } } |
再举一个例子,要确定在(取消)检查此项目后该集合是否为空:
1 2 3 4 5 6 7 8 9 | 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 ... } |
关于此,有很多相关的StackOverflow帖子...以及Branimir的解决方案,这里还有两个简单的解决方案:
在ItemCheck上延迟执行(也在此处):
1 2 3 4 5 | void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) { this.BeginInvoke((MethodInvoker) ( () => Console.WriteLine(checkedListBox1.SelectedItems.Count))); } |
使用MouseUp事件:
1 2 3 4 | void checkedListBox1_MouseUp(object sender, MouseEventArgs e) { Console.WriteLine(checkedListBox1.SelectedItems.Count); } |
我倾向于第一种选择,因为第二种选择会导致误报(即触发次数过多)。
我尝试了一下,它起作用了:
1 2 3 4 5 6 7 8 9 10 11 12 | 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(); } |
从
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /// <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); } |
经过一些测试,我可以看到事件ItemCheck之后触发了事件SelectedIndexChanged。保持属性CheckOnClick为True
最佳编码
尽管不理想,但是可以使用传递给
您甚至可以创建一个新事件,该事件在检查项目后触发,这将为您提供所需的确切信息。
这行得通,但不确定它有多优雅!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 2 | CheckedListBox ctrl = new CheckedListBox(); ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e))); |
1 2 3 4 | private void CheckedItemsChanged(object sender, EventArgs e) { DoYourThing(); } |
不知道这是否适用,但是我想使用一个复选框来过滤结果。因此,当用户选中和取消选中项目时,我希望列表显示\隐藏项目。
我遇到了一些问题,这使我想到了这个职位。只是想分享我的做法,没有什么特别的。
注意:我有CheckOnClick = true,但如果没有
我使用的事件是" SelectedIndexChanged"
我使用的枚举是" .CheckedItems"
这给出了我认为可以预期的结果。如此简化,可以归结为...。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 } } } |
我尝试了一下,它起作用了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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); } } |
通过列表的索引
在正常情况下,当我们检查一项时,在引发事件处理程序之前,该项目的检查状态将更改。
但是CheckListBox具有不同的行为:在项目的检查状态更改之前引发事件处理程序,这使得更正我们的工作变得困难。
我认为,要解决此问题,我们应该推迟事件处理程序。
1 2 3 4 5 6 7 8 9 | 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());} |
我使用计时器来解决此问题。通过ItemCheck事件启用计时器。在"计时器的滴答"事件中采取措施。
无论通过鼠标单击还是按下空格键检查项目,该方法均有效。我们将利用这样一个事实,即刚刚选中(或未选中)的项目始终是Selected Item。
计时器的间隔可以低至1。在Tick事件引发时,将设置新的Checked状态。
此VB.NET代码说明了这一概念。您可以采用多种变体。您可能希望增加计时器的间隔,以允许用户在执行操作之前更改多个项目的检查状态。然后在Tick事件中,顺序传递列表中的所有项目,或使用其CheckedItems集合采取适当的操作。
这就是为什么我们首先在ItemCheck事件中禁用Timer的原因。禁用然后启用将导致间隔时间重新开始。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 |