Mam CheckedListBox, w którym chcę zdarzenie po sprawdzeniu elementu, aby móc używać CheckedItems z nowym stanem.
Ponieważ ItemChecked jest uruchamiany przed aktualizacją CheckedItems, nie będzie działać po wyjęciu z pudełka.
Jakiego rodzaju metody lub zdarzenia mogę użyć, aby otrzymać powiadomienie o aktualizacji CheckedItems?
c#
winforms
checkedlistbox
hultqvist
źródło
źródło
if not item = checkedListBox1.Items[e.Index].ToString()
Jest wiele powiązanych postów StackOverflow na ten temat ... Oprócz rozwiązania Branimira , oto dwa prostsze:
Opóźnione wykonanie w ItemCheck (również tutaj ):
void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) { this.BeginInvoke((MethodInvoker) ( () => Console.WriteLine(checkedListBox1.SelectedItems.Count))); }
Korzystanie ze zdarzenia MouseUp :
void checkedListBox1_MouseUp(object sender, MouseEventArgs e) { Console.WriteLine(checkedListBox1.SelectedItems.Count); }
Wolę pierwszą opcję, ponieważ druga skutkowałaby fałszywymi alarmami (tj. Zbyt częstym odpalaniem).
źródło
The check state is not updated until after the ItemCheck event occurs
. Inne wydarzenie lub niearbitralne obejście byłoby fajne IMO.Spróbowałem tego i zadziałało:
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(); }
źródło
ItemChecked
wydarzenie i nikt nigdy nie wspomniał, że nie istnieje.ItemChecking
,ItemChecked
, a następnie można użyć drugiego z nich. Ale jeśli zaimplementowano tylko jeden (ItemCheck
), robi to poprawnie, tj. Uruchamia zdarzenie, zanim wartość zostanie sprawdzona z nową wartością i indeksem podanym jako parametry. Kto chce wydarzenia „po zmianie”, może po prostu skorzystać z powyższego. Jeśli zasugerujesz coś Microsoftowi, zasugeruj nowe wydarzenieItemChecked
, nie zmieniając istniejącego: zobacz odpowiedźWyprowadzaj
CheckedListBox
i wdrażaj/// <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); }
źródło
BeginInvoke
z rozwiązania z drugiej odpowiedzi.Chociaż nie jest to idealne rozwiązanie, możesz obliczyć CheckedItems przy użyciu argumentów, które są przekazywane do
ItemCheck
zdarzenia. Jeśli spojrzysz na ten przykład w witrynie MSDN , możesz ustalić, czy nowo zmieniony element został zaznaczony, czy niezaznaczony, co pozostawia Cię w odpowiedniej pozycji do pracy z elementami.Możesz nawet utworzyć nowe wydarzenie, które będzie uruchamiane po sprawdzeniu przedmiotu, co da ci dokładnie to, czego chciałeś, jeśli chcesz.
źródło
Po kilku testach mogłem zobaczyć, że zdarzenie SelectedIndexChanged jest wyzwalane po zdarzeniu ItemCheck. Zachowaj właściwość CheckOnClick True
Najlepsze kodowanie
źródło
To działa, ale nie jestem pewien, jak eleganckie jest!
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
źródło
Nie wiem, czy to dotyczy, ale chciałem użyć pola listy kontrolnej do filtrowania wyników. Ponieważ użytkownik sprawdzał i odznaczał pozycje, chciałem, aby lista pokazywała \ ukrywała pozycje.
Miałem pewne problemy, które doprowadziły mnie do tego postu. Chciałem tylko podzielić się tym, jak to zrobiłem bez niczego specjalnego.
Uwaga: mam CheckOnClick = true, ale prawdopodobnie nadal działałoby bez
Zdarzenie, którego używam, to „ SelectedIndexChanged ”
wyliczenie, którego używam, to „ .CheckedItems ”
To daje rezultaty, których myślę, że możemy się spodziewać. Tak uproszczony, sprowadza się do ...
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 } } }
źródło
Zakładając, że chcesz zachować argumenty z,
ItemCheck
ale otrzymasz powiadomienie po zmianie modelu, powinno to wyglądać tak:CheckedListBox ctrl = new CheckedListBox(); ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e)));
Gdzie
CheckedItemsChanged
może być:private void CheckedItemsChanged(object sender, EventArgs e) { DoYourThing(); }
źródło
Spróbowałem tego i zadziałało:
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); } }
określić według indeksu listy.
źródło
Aby rozwiązać ten problem, używam timera. Włącz licznik czasu za pomocą zdarzenia ItemCheck. Podejmij działanie w wydarzeniu Timer's Tick.
Działa to niezależnie od tego, czy element jest zaznaczony kliknięciem myszy, czy naciśnięciem spacji. Skorzystamy z faktu, że pozycja właśnie zaznaczona (lub odznaczona) jest zawsze pozycją Wybraną.
Przedział czasowy może wynosić zaledwie 1. Zanim zdarzenie Tick zostanie podniesione, nowy status Checked zostanie ustawiony.
Ten kod VB.NET przedstawia koncepcję. Istnieje wiele odmian, które możesz zastosować. Możesz chcieć zwiększyć Interwał licznika czasu, aby umożliwić użytkownikowi zmianę stanu sprawdzania kilku elementów przed podjęciem działania. Następnie w zdarzeniu Tick wykonaj sekwencyjne przekazanie wszystkich elementów na liście lub użyj jego kolekcji CheckedItems, aby podjąć odpowiednią akcję.
Dlatego najpierw wyłączamy Timer w zdarzeniu ItemCheck. Wyłącz, a następnie Włącz powoduje ponowne rozpoczęcie okresu interwału.
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
źródło
W normalnym zachowaniu, gdy sprawdzamy jeden element, stan sprawdzenia elementu zmieni się przed wywołaniem procedury obsługi zdarzenia. Ale CheckListBox ma inne zachowanie: program obsługi zdarzeń jest wywoływany przed zmianą stanu sprawdzania elementu, co utrudnia poprawianie naszych zadań.
Moim zdaniem, aby rozwiązać ten problem, powinniśmy odroczyć obsługę zdarzeń.
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());}
źródło