我有一个CheckedListBox,我希望在检查一个项目后在此事件,以便可以将CheckedItems与新状态一起使用。
由于在CheckedItems更新之前就触发了ItemChecked,因此无法立即使用。
更新CheckedItems时可以使用哪种方法或事件进行通知?
Answers:
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
...
}
关于此,有很多相关的StackOverflow帖子...以及Branimir的解决方案,这里还有两个简单的解决方案:
在ItemCheck上延迟执行(也在此处):
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);
}
我倾向于第一种选择,因为第二种选择会导致误报(即触发次数过多)。
我尝试了一下,它起作用了:
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();
}
ItemChecked
活动,而且从未有人提出过该活动不存在。
ItemChecking
,,ItemChecked
则可以使用后一个。但是,如果仅实现一个(ItemCheck
),则它可以正确地执行操作,即在使用新值和作为参数提供的索引检查值之前触发事件。谁想要“更改后”事件,他们都可以简单地使用以上内容。如果向微软提出建议,则建议提出一个新事件 ItemChecked
,而不是改变现有事件:请参阅diimdeep的答案
派生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);
}
经过一些测试,我可以看到事件ItemCheck之后触发了SelectedIndexChanged事件。保持属性CheckOnClick为True
最佳编码
这行得通,但不确定它有多优雅!
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
不知道这是否适用,但是我想使用一个复选框来过滤结果。因此,当用户选中和取消选中项目时,我希望列表显示\隐藏项目。
我遇到了一些问题,这使我想到了这个职位。只是想分享我的做法,没有什么特别的。
注意:我有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
}
}
}
我使用计时器来解决此问题。通过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
在正常情况下,当我们检查一项时,在引发事件处理程序之前,该项目的检查状态将更改。但是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());}
我尝试了一下,它起作用了:
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);
}
}
通过列表的索引确定。
if not item = checkedListBox1.Items[e.Index].ToString()