Какое событие CheckedListBox срабатывает после проверки элемента?


96

У меня есть CheckedListBox, где мне нужно событие после проверки элемента, чтобы я мог использовать CheckedItems с новым состоянием.

Поскольку ItemChecked запускается до обновления CheckedItems, он не будет работать из коробки.

Какой метод или событие можно использовать, чтобы получать уведомления об обновлении CheckedItems?

Ответы:


89

Вы можете использовать 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
        ...
}

3
в первую очередь для каждого нам может потребоваться добавить по одному условию if ..if not item = checkedListBox1.Items[e.Index].ToString()
Ленин Радж Раджасекаран

8
Проблема в том, что событие ItemCheck запускается до обработки проверки. Ваше решение будет включать ведение собственного списка, по сути дублирующего стандартный код. Первое предложение Дунка (отложенное выполнение на ItemCheck) - это, по-моему, самый чистый ответ на вопрос о phq, потому что он не требует дополнительной обработки.
Беренд Энгельбрехт

35

По этому поводу есть много связанных сообщений StackOverflow ... Помимо решения Бранимира , вот еще два простых:

Отложенное выполнение на 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. Может быть, это глупый комментарий, ребята .. но почему об этой ОШИБКЕ сообщается в теме, начатой ​​в 2010 году, и не решенной в 2018?
Goodies

1
@Goodies Согласен, хотя я предполагаю, что это может сломать много кода, если Microsoft изменит поведение сейчас. В документах явно указано The check state is not updated until after the ItemCheck event occurs. Другое событие или непроизвольное обходное решение было бы неплохо, ИМО.
Дунк

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

Хотя это не ошибка по определению, я думаю, что это должно быть реализовано, если вы согласны, рассмотрите возможность поддержки этого отчета об ошибке, нажав +1: connect.microsoft.com/VisualStudio/feedback/details/1759293
SCBuergel.eth

@ Себастьян - здесь не просите исправить. Любое «исправление» этого нарушило бы существующие решения. Если два события: ItemChecking, ItemChecked, то вы можете использовать один последний. Но если реализован только один ( ItemCheck), он выполняет все правильно, т.е. запускает событие до того, как значение будет проверено с новым значением и индексом, предоставленным в качестве параметров. Кто хочет событие «после изменения», он может просто использовать вышеуказанное. Если вы предложите что-то Microsoft, предложите новое мероприятие ItemChecked , а не изменение существующего: см . Ответ
diimdeep

Подобно этому, но одна небольшая альтернатива, которую я использую все время, - это просто установить какой-то флаг «пропуска», чтобы SetItemCheckState не запускал повторно одно и то же событие. Либо простой глобальный, либо то, что я люблю делать, это проверять тег. например, оберните действие в If myCheckListBox.Tag! = null, а затем вместо Event 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);
}

Это много дополнительного кода, который вы можете пропустить при использовании BeginInvokeрешения из второго ответа.
Лукас Фрончик

4

Хотя это и не идеально, вы можете вычислить CheckedItems, используя аргументы, которые передаются ItemCheckсобытию. Если вы посмотрите на этот пример в MSDN , вы сможете определить, был ли недавно измененный элемент отмечен или снят, что оставляет вас в подходящем положении для работы с элементами.

Вы даже можете создать новое событие, которое срабатывает после проверки элемента, что даст вам именно то, что вы хотели, если хотите.


1
У вас есть какое-нибудь конкретное представление о том, как можно создать это новое событие, как я могу узнать, были ли обновлены CheckedItems после события ItemChecke?
hultqvist

4

После некоторых тестов я увидел, что событие SelectedIndexChanged запускается после события ItemCheck. Сохраните свойство CheckOnClick True

Лучшее кодирование


Вы правы, это самый простой способ. Но это все равно что-то вроде взлома, потому что это недокументированное и НЕОЖИДАННОЕ поведение. Любой новичок в Microsoft может подумать: ну, а зачем запускать SelectedIndexChanged, когда изменяется только Checkstate. Давайте оптимизируем это. И Bang использует ваш код :(
Рольф

Кроме того, SelectedIndexChanged не срабатывает при программном изменении состояния проверки.
Рольф

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

SelectedIndexChanged не срабатывает при вызове SetItemChecked для проверки или снятия отметки с элемента в коде.
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

Я попробовал это, и это сработало:

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

определить по индексу списка.


-1

Я использую таймер для решения этой проблемы. Включите таймер с помощью события ItemCheck. Выполните действие в событии Timer's Tick.

Это работает независимо от того, отмечен ли элемент щелчком мыши или нажатием клавиши пробела. Мы воспользуемся тем фактом, что элемент, только что отмеченный (или не отмеченный), всегда является выбранным элементом.

Интервал таймера может составлять всего 1. К моменту возникновения события Tick будет установлен новый статус Checked.

Этот код VB.NET демонстрирует концепцию. Вы можете использовать множество вариантов. Вы можете увеличить интервал таймера, чтобы пользователь мог изменить статус проверки для нескольких элементов, прежде чем предпринимать какие-либо действия. Затем в событии Tick выполните последовательный проход всех элементов в списке или используйте его коллекцию CheckedItems для выполнения соответствующих действий.

Вот почему мы сначала отключаем таймер в событии ItemCheck. Отключить, а затем Включить - перезапускает период интервала.

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

1
Спасибо за то, что поделились. С другой стороны, возможно, вы сможете узнать о лучших решениях из других ответов. Использование таймера относительно сложно, и в данном случае это неправильный инструмент для работы, потому что вы фактически уже получаете новые значения в качестве параметров. Таким образом, вы можете использовать этот ответ для одноразового решения или этот для систематического решения. Конвертируйте их из C # в VB с помощью одного из инструментов онлайн-конвертации.
miroxlav

-1

В нормальном поведении, когда мы проверяем один элемент, состояние проверки элемента изменится до того, как будет вызван обработчик события. Но 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());}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.