Какое событие CheckedListBox срабатывает после проверки элемента?
У меня есть CheckedListBox, где я хочу событие после проверки элемента, чтобы я мог использовать CheckedItems с новым состоянием.
Поскольку ItemChecked запускается до обновления CheckedItems, он не будет работать сразу после установки.
Какой метод или событие я могу использовать, чтобы получать уведомления при обновлении CheckedItems?
18 ответов
Вы можете использовать ItemCheck
событие, если вы также проверите новое состояние элемента, по которому осуществляется щелчок. Это доступно в аргументах события, как 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
...
}
Об этом много сообщений из Stackru... Помимо решения Бранимира, есть еще два простых:
Отложенное выполнение на 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);
}
Я предпочитаю первый вариант, так как второй может привести к ложным срабатываниям (т. Е. Слишком часто).
Я попробовал это, и это сработало:
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();
}
Вытекают из 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);
}
После некоторых тестов я увидел, что событие SelectedIndexChanged запускается после события ItemCheck. Сохраните свойство CheckOnClick True
Лучшее кодирование
Хотя это и не идеально, вы можете вычислить CheckedItems, используя аргументы, которые передаются в ItemCheck
событие. Если вы посмотрите на этот пример в MSDN, вы сможете определить, был ли установлен новый измененный элемент или нет, что оставляет вас в подходящем положении для работы с элементами.
Вы даже можете создать новое событие, которое срабатывает после проверки элемента, которое даст вам именно то, что вы хотели, если хотите.
Я попробовал это, и это сработало:
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);
}
}
определить по индексу списка.
Это работает, не уверен, насколько это элегантно!
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
но получить уведомление после изменения модели должно выглядеть так:
CheckedListBox ctrl = new CheckedListBox();
ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e)));
куда CheckedItemsChanged
может быть:
private void CheckedItemsChanged(object sender, EventArgs e)
{
DoYourThing();
}
Если, как и я, вы пытались использовать Selection как один индикатор (элемент, выбранный пользователем), а пользователь хотел изменить галочку, то я нашел хитрое решение.
Переменные формы Private IsTicked As Boolean = FalsePrivate ListIndex = -1
поставить таймер на страницу, моя называется tmrBan У меня есть CheckBoxList с именем clbFTI
Затем создайте событие Click для вас CheckBoxList
Частная подпрограмма clbFTI_Click(отправитель как объект, e как EventArgs) обрабатывает lbFTI.MouseClick
ListIndex = sender.SelectedIndex
IsTicked = clbFTI.SelectedIndices.Contains(ListIndex)
tmrBan.Interval = 10
tmrBan.Enabled = True
End Sub
Видите ли вы, что это произойдет ... затем создайте галочку Event для вашего таймера
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles tmrBan.Tick
clbFTI.SetItemChecked(ListIndex, IsTicked)
End Sub
Вы увидите мерцание галочки, но поиграйте с интервалом таймера, чтобы сделать это лучше.
Я столкнулся с аналогичной проблемой: при щелчке элемента состояние должно быть преобразовано из отмеченного/не проверенного в противоположное. Здесь я публикую событие и проверку и изменение:
CheckedListBox ChkLBox;
private void CheckedListBox_SelectedIndexChanged(object sender, EventArgs e)
{
int SelectedIndex = ChkLBox.SelectedIndex; //
var Item = ChkLBox.Items[SelectedIndex];
bool IsChecked = (ChkLBox.GetItemChecked(ChkLBox.SelectedIndex));
ChkLBox.SetItemChecked(ChkLBox.Items.IndexOf(Item), !IsChecked);
}
Я решил просто не заботиться о чувствах элемента управления и обрабатывать событие так, как будто смена флажка действительно уже произошла. Все, что вам нужно сделать, это взять
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);
}
Результат функционально идентичен гипотетическому желаемому случаю, когда событие запускается только после изменения.
Вы имеете в виду checkboxlist, а не checklistbox? Если да, то рассматриваемым событием будет SelectedIndexChanged.
например, заголовок определения обработчика в VB:
Protected Sub cblStores_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cblStores.SelectedIndexChanged
Эта ветка возникает, когда вы ищете решения для VB dotnet (.NET), поэтому я просто приведу найденное решение здесь (Invoke).
Private Sub CheckListBox1_ItemCheck(sender As Object, e As EventArgs) Handles checkListBox1.ItemCheck
Me.BeginInvoke(New MethodInvoker(AddressOf CustomFunctionHere))
End Sub
Private Sub CustomFunctionHere()
'Do something
End Sub
РЕДАКТИРОВАТЬ: после использования Invoke некоторое время я понял, что это не лучшее решение для моего проекта. Работает, но порядок операций скинет. Вместо этого я создал метод для расчета «фиксированного» количества с использованием SelectedItems.Count вместе со старыми и новыми значениями выбора, чтобы определить, следует ли добавить или вычесть единицу из общего количества.
Версия VB.NET ответа Дунка на
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
В обычном поведении, когда мы проверяем один элемент, состояние проверки элемента изменится до повышения обработчика события. Но 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());}
Я использую таймер для решения этой проблемы. Включить таймер через событие ItemCheck. Примите меры в событии Timer Tick.
Это работает независимо от того, проверяется ли элемент щелчком мыши или нажатием пробела. Мы воспользуемся тем, что только что отмеченный (или не отмеченный) элемент всегда является выбранным элементом.
Интервал таймера может составлять всего 1. К тому времени, когда событие Tick будет вызвано, будет установлен новый статус Checked.
Этот код VB.NET демонстрирует концепцию. Есть много вариантов, которые вы можете использовать. Вы можете увеличить интервал таймера, чтобы пользователь мог изменить статус проверки нескольких элементов, прежде чем предпринимать какие-либо действия. Затем в событии Tick выполните последовательную передачу всех элементов в списке или используйте его коллекцию CheckedItems для выполнения соответствующих действий.
Вот почему мы сначала отключаем Timer в событии ItemCheck. Disable затем Enable заставляет период интервала перезапускаться.
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