我有一个CheckedListBox,在项目被检查之后我需要一个事件,以便我可以使用具有新状态的CheckedItems。
由于在CheckedItems更新之前触发了ItemChecked,因此它不会开箱即用。
更新CheckedItems时,我可以使用哪种方法或事件来通知?
答案 0 :(得分:74)
如果您还检查正在点击的项目的新状态,则可以使用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
...
}
答案 1 :(得分:28)
此处有很多相关的StackOverflow帖子......除Branimir's解决方案外,还有两个更简单的帖子:
Delayed execution on ItemCheck(也here):
void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
this.BeginInvoke((MethodInvoker) (
() => Console.WriteLine(checkedListBox1.SelectedItems.Count)));
}
void checkedListBox1_MouseUp(object sender, MouseEventArgs e)
{
Console.WriteLine(checkedListBox1.SelectedItems.Count);
}
我更喜欢第一种选择,因为第二种选择会导致误报(即经常射击)。
答案 2 :(得分:20)
我试过这个并且有效:
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();
}
答案 3 :(得分:9)
从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 :(得分:4)
虽然不理想,但您可以使用传递给ItemCheck
事件的参数来计算CheckedItems。如果您查看此example on MSDN,您可以确定是否已选中或取消选中新更改的项目,这使您处于合适的位置以处理项目。
您甚至可以创建一个在检查项目后触发的新事件,如果您愿意,这将为您提供您想要的内容。
答案 5 :(得分:4)
经过一些测试,我可以看到事件ItemCheck后触发事件SelectedIndexChanged。保持属性CheckOnClick True
最佳编码
答案 6 :(得分: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
答案 7 :(得分:1)
假设你想保留ItemCheck
的参数,但在模型改变后得到通知,它应该是这样的:
CheckedListBox ctrl = new CheckedListBox();
ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e)));
CheckedItemsChanged
可能是:
private void CheckedItemsChanged(object sender, EventArgs e)
{
DoYourThing();
}
答案 8 :(得分:0)
我使用Timer来解决这个问题。通过ItemCheck事件启用计时器。在Timer的Tick事件中采取行动。
无论是通过鼠标单击还是按空格键检查项目,都可以使用此功能。我们将利用刚检查(或未检查)的项目始终是选定项目的事实。
定时器的间隔可以低至1.当Tick事件被提升时,将设置新的Checked状态。
这个VB.NET代码显示了这个概念。您可以使用许多变体。您可能希望增加计时器的间隔,以允许用户在采取措施之前更改多个项目的检查状态。然后在Tick事件中,对List中的所有Items进行顺序传递,或使用其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
答案 9 :(得分: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());}
答案 10 :(得分: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);
}
}
通过列表的索引确定。
答案 11 :(得分:0)
您是指复选框列表,而不是复选框列表吗?如果是这样,则相关事件将为SelectedIndexChanged。
例如VB中的处理程序定义头:
Protected Sub cblStores_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cblStores.SelectedIndexChanged
答案 12 :(得分: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);
}
结果在功能上与假设的期望情况相同,即事件仅在更改后触发。
答案 13 :(得分:0)
VB.NET 版本的 Dunc's answer 到 BeginInvoke
处理程序,以便检查项目。
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
答案 14 :(得分:0)
如果像我一样,您试图将选择用作一个指标(用户选择的项目),而用户想要更改勾号,那么我找到了一个偷偷摸摸的解决方案。
Form variables
Private IsTicked As Boolean = False
Private ListIndex = -1
在页面上创建一个计时器。例如,我的名为 tmrBan
,而我有一个名为 CheckBoxList
的 clbFTI
。
然后,为您的 CheckBoxList
创建一个 Click 事件。
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 以使其更适合您的情况。