Яка подія CheckedListBox запускається після перевірки елемента?


96

У мене є CheckedListBox, де я хочу подію після перевірки елемента, щоб я міг використовувати CheckedItems з новим станом.

Оскільки ItemChecked запускається до оновлення 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 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 був саме тим, що мені потрібно, оскільки моя подія насправді викликала інтерфейс, який навіть не уявляв, з яким видом контролю він має справу. Прийнята відповідь працює лише у тих випадках, коли логіка може виконуватися в обробнику подій або щось, що викликається безпосередньо з обробника подій. Для мене це було не так. Дякуємо за це чудове, але просте рішення.
Джессі

Thx, перший варіант з BeginInvoke працює для мене. Можливо, дурний коментар, люди .. але чому про цю ПОМИЛКУ повідомляють у темі, розпочатій у 2010 році, не вирішеній у 2018 році ??
Goodies

1
@Goodies погодився, хоча, мабуть, це може зламати багато коду, якби Microsoft змінила поведінку зараз. У документах явно зазначено The check state is not updated until after the ItemCheck event occurs. Інша подія чи не довільний обхідний шлях було б непогано IMO.
Данк,

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.

@Sebastian - не питайте тут виправлення. Будь-яке "виправлення" цього може порушити вихідні рішення. Якщо дві події: ItemChecking, ItemChecked, то ви можете використовувати один останній. Але якщо реалізовано лише один ( ItemCheck), він робить все правильно, тобто активація події до того, як значення перевіряється новим значенням та індексом, що подаються як параметри. Хто хоче подію "після змін", він може просто скористатися вищезазначеним. Якщо ви щось пропонуєте Microsoft, то запропонуйте нову подію ItemChecked , не змінюючи існуючу: див . Відповідь
diimdeep

Ось так, але одна незначна альтернатива, яку я постійно використовую, - просто встановити якийсь прапор "пропустити", щоб SetItemCheckState не запускав повторно ту саму подію. Або простий глобальний, або те, що я люблю робити, - це переконатися в тезі. наприклад, оберніть дію в if myCheckListBox.Tag! = null, а потім замість Події Видалити \ Додати, просто встановіть тег на щось (навіть порожній рядок), а потім поверніть на 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рішення з другої відповіді.
ƨukasƨ Fronczyk

4

Хоча це не ідеально, ви можете обчислити CheckedItems, використовуючи аргументи, які передаються до ItemCheckподії. Якщо ви подивитесь на цей приклад на MSDN , ви зможете визначити, чи був нещодавно змінений елемент позначеним чи не позначеним, що залишає вас у відповідному положенні для роботи з елементами.

Ви навіть можете створити нову подію, яка спрацьовує після перевірки елемента, яка при бажанні дасть вам саме те, що ви хотіли.


1
Чи маєте ви якесь конкретне уявлення про те, як можна створити цю нову подію, як я можу знати, коли CheckedItems оновлено після події ItemChecke?
hultqvist

4

Після деяких тестів я міг побачити, що подія SelectedIndexChanged запускається після події ItemCheck. Зберігайте властивість CheckOnClick True

Найкраще кодування


Ви маєте рацію, це найпростіший спосіб. Але це все одно щось на кшталт хакерства, оскільки це недокументована та непередбачувана поведінка. Будь-який першокурсник у Microsoft може подумати: ну ну, навіщо запускати SelectedIndexChanged, коли змінюється лише Checkstate. Давайте оптимізуємо це. І Банг передає ваш код :(
Рольф

Крім того, 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. Вживайте заходів у випадку з таймером.

Це працює, незалежно від того, чи перевіряється елемент клацанням миші або натисканням пробілу. Ми скористаємось тим, що елемент, який щойно перевірили (або не позначили), завжди є вибраним елементом.

Інтервал таймера може бути не меншим 1. До моменту підняття події Тик буде встановлено новий статус Перевірено.

Цей код 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.