Tengo un CheckedListBox donde quiero un evento después de que se verifique un elemento para poder usar CheckedItems con el nuevo estado.
Dado que ItemChecked se activa antes de que se actualice CheckedItems, no funcionará de inmediato.
¿Qué tipo de método o evento puedo utilizar para recibir una notificación cuando se actualice CheckedItems?
c#
winforms
checkedlistbox
hultqvist
fuente
fuente
if not item = checkedListBox1.Items[e.Index].ToString()
Hay muchas publicaciones de StackOverflow relacionadas sobre esto ... Además de la solución de Branimir , aquí hay dos más simples:
Ejecución retrasada en ItemCheck (también aquí ):
void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) { this.BeginInvoke((MethodInvoker) ( () => Console.WriteLine(checkedListBox1.SelectedItems.Count))); }
Usando el evento MouseUp :
void checkedListBox1_MouseUp(object sender, MouseEventArgs e) { Console.WriteLine(checkedListBox1.SelectedItems.Count); }
Prefiero la primera opción, ya que la segunda daría lugar a falsos positivos (es decir, disparar con demasiada frecuencia).
fuente
The check state is not updated until after the ItemCheck event occurs
. Un evento diferente o una solución alternativa no arbitraria sería bueno en mi opinión.Probé esto y funcionó:
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(); }
fuente
ItemChecked
evento y nadie dijo que no existía.ItemChecking
,ItemChecked
, entonces se podría utilizar último. Pero si solo se implementa uno (ItemCheck
), está haciendo las cosas correctamente, es decir, disparando el evento antes de que se verifique el valor con el nuevo valor y el índice proporcionado como parámetros. Quien quiera el evento "después del cambio", simplemente puede usar el anterior. Si sugiere algo a Microsoft, sugiera un nuevo eventoItemChecked
, que no cambie el existente: vea la respuesta deDerivar
CheckedListBox
e implementar/// <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); }
fuente
BeginInvoke
solución de la segunda respuesta.Aunque no es lo ideal, puede calcular CheckedItems utilizando los argumentos que se pasan al
ItemCheck
evento. Si observa este ejemplo en MSDN , puede averiguar si el elemento recién cambiado se ha marcado o desmarcado, lo que lo deja en una posición adecuada para trabajar con los elementos.Incluso podría crear un nuevo evento que se active después de que se verifique un elemento, lo que le daría exactamente lo que deseaba si lo deseaba.
fuente
Después de algunas pruebas, pude ver que el evento SelectedIndexChanged se activa después del evento ItemCheck. Mantenga la propiedad CheckOnClick True
Mejor codificación
fuente
¡Esto funciona, aunque no estoy seguro de lo elegante que es!
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
fuente
No sé si esto se aplica, pero quería usar una casilla de verificación para filtrar los resultados. Entonces, mientras el usuario marcaba y desmarcaba los elementos, quería que la lista mostrara / ocultara los elementos.
Estaba teniendo algunos problemas que me llevaron a esta publicación. Solo quería compartir cómo lo hice sin nada especial.
Nota: tengo CheckOnClick = true pero probablemente aún funcionaría sin
El evento que uso es " SelectedIndexChanged "
la enumeración que utilizo es " .CheckedItems "
Esto da los resultados que creo que podemos esperar. Tan simplificado que se reduce a ...
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 } } }
fuente
Suponiendo que desea conservar los argumentos de
ItemCheck
pero recibir una notificación después de que se haya cambiado el modelo, debería verse así:CheckedListBox ctrl = new CheckedListBox(); ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e)));
Dónde
CheckedItemsChanged
podría estar:private void CheckedItemsChanged(object sender, EventArgs e) { DoYourThing(); }
fuente
Probé esto y funcionó:
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); } }
determinar por índice de la lista.
fuente
Utilizo un temporizador para resolver este problema. Habilite el temporizador a través del evento ItemCheck. Actúa en el evento Timer's Tick.
Esto funciona ya sea que el elemento se verifique mediante un clic del mouse o presionando la barra espaciadora. Aprovecharemos el hecho de que el elemento recién marcado (o desmarcado) es siempre el elemento seleccionado.
El intervalo del temporizador puede ser tan bajo como 1. Para cuando se active el evento Tick, se establecerá el nuevo estado Checked.
Este código VB.NET muestra el concepto. Hay muchas variaciones que puede emplear. Es posible que desee aumentar el intervalo del temporizador para permitir que el usuario cambie el estado de verificación en varios elementos antes de tomar medidas. Luego, en el evento Tick, haga un pase secuencial de todos los elementos de la lista o use su colección CheckedItems para tomar la acción apropiada.
Es por eso que primero deshabilitamos el temporizador en el evento ItemCheck. Desactivar y luego Activar hace que el período de intervalo se reinicie.
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
fuente
En el comportamiento normal, cuando verificamos un elemento, el estado de verificación del elemento cambiará antes de que se active el controlador de eventos. Pero un CheckListBox tiene un comportamiento diferente: el controlador de eventos se genera antes de que cambie el estado de verificación del elemento y eso dificulta la corrección de nuestros trabajos.
En mi opinión, para resolver este problema, deberíamos aplazar el controlador de eventos.
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());}
fuente