2010-09-08 18 views

Respuesta

55

Puede usar el evento ItemCheck, si también verifica el nuevo estado del elemento que se está haciendo clic. Esto está disponible en el evento args, como e.NewValue. Si se comprueba NewValue, incluye el elemento actual junto con la colección adecuada en su lógica:

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

     foreach (string item in checkedItems) 
     { 
      ... 
     } 
    } 

Como otro ejemplo, para determinar si la colección estará vacía después de este artículo es (des) verificado:

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

en el primero para cada uno, podríamos necesitar agregar una condición if .. 'if not item = checkedListBox1.Items [e.Index] .ToString()' – emaillenin

+8

El problema es que el evento ItemCheck es f antes de que se procese el cheque. Su solución implicaría mantener su propia lista, esencialmente duplicando el código estándar. La primera sugerencia de Dunc (ejecución retrasada en ItemCheck) es la respuesta más clara a la pregunta de phq, ya que no requiere ningún manejo adicional. –

4

Aunque no es ideal, puede calcular CheckedItems utilizando los argumentos que se pasan al evento ItemCheck. Si mira esto example on MSDN, puede determinar si el elemento recientemente cambiado ha sido marcado o no, lo que lo deja en una posición adecuada para trabajar con los artículos.

Incluso podría crear un nuevo evento que se active después de marcar un elemento, lo que le proporcionaría exactamente lo que deseaba si lo deseaba.

+1

¿Tiene alguna idea específica sobre cómo se podría crear este nuevo evento? ¿Cómo puedo saber cuándo se han actualizado CheckedItems después del evento ItemChecke? – hultqvist

22

Hay un montón de mensajes StackOverflow relacionados en esta ... Así como Branimir's solución, aquí hay dos más simples:

Delayed execution on ItemCheck (también here):

void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) 
    { 
     this.BeginInvoke((MethodInvoker) (
      () => Console.WriteLine(checkedListBox1.SelectedItems.Count))); 
    } 

Using the MouseUp event:

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, disparando muy a menudo).

+8

El segundo método también se perderá elementos marcados o desmarcados a través del teclado. –

+1

BeginInvoke era exactamente lo que necesitaba ya que mi evento llamaba a una interfaz, que no tenía idea de qué tipo de control estaba tratando. La respuesta aceptada solo funciona en casos mientras que la lógica se puede realizar dentro del controlador de eventos, o algo llamado directamente desde el controlador de eventos. Este no fue el caso para mí. Gracias por esta increíble pero simple solución. – Jesse

2

Esto funciona, pero 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 
15

yo probamos este 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();   
} 
+5

Esto! ... debería ser la respuesta correcta, lo cual es muy desafortunado. Este es un hack ridículo que funciona porque alguien en M $ olvidó implementar el evento 'ItemChecked', y nadie se dirigió a que no existía. – RLH

+0

Aunque no es por definición un error, creo que esto debería implementarse, si está de acuerdo, considere respaldar este informe de error haciendo clic en +1: https://connect.microsoft.com/VisualStudio/feedback/details/1759293 – Sebastian

+0

@Sebastian - no pidas una solución aquí. Cualquier "solución" de esto rompería las soluciones existentes. Si hubo dos eventos: 'ItemChecking',' ItemChecked', entonces podría usar el último. Pero si solo se implementa uno ('ItemCheck'), se están haciendo las cosas correctamente, es decir, se activa el evento * before * value con el nuevo valor y el índice suministrados como parámetros. Quien quiera el evento "después del cambio", simplemente puede usar lo anterior. Si le sugiere algo a Microsoft, sugiera un ** nuevo evento ** 'ItemChecked', sin cambiar el existente: vea [la respuesta de diimdeep] (http://stackoverflow.com/a/18487539/2392157) – miroxlav

8

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

Después de algunas pruebas, pude ver que el evento SelectedIndexChanged se activa después del evento ItemCheck . Mantener la propiedad CheckOnClick verdadera

mejor codificación

+0

Tienes razón, esta es la forma más fácil. Pero todavía es algo así como un truco, porque es un comportamiento indocumentado e INESPERADO. Cualquier estudiante de primer año en Microsoft podría pensar: bueno, ¿por qué disparar SelectedIndexChanged cuando solo cambia el Checkstate? Optimicemos eso. Y Bang va con el código :( – Rolf

+0

Además, SelectedIndexChanged no se activa al cambiar el estado de verificación mediante programación. – Rolf

-1

uso un temporizador para resolver este problema. Habilite el temporizador mediante el evento ItemCheck. Actúa en el evento Timer's Tick.

Esto funciona si el elemento se comprueba con un clic del mouse o presionando la barra espaciadora.Aprovecharemos el hecho de que el artículo que acaba de marcarse (o no) siempre es el Elemento seleccionado.

El intervalo del temporizador puede ser tan bajo como 1. Para cuando se produce el evento Tick, se establece el nuevo estado comprobado.

Este código de VB.NET muestra el concepto. Hay muchas variaciones que puede emplear. Es posible que desee aumentar el intervalo del temporizador para permitir al usuario cambiar 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 en la lista o use su colección CheckedItems para tomar las medidas apropiadas.

Es por eso que primero desactivamos el temporizador en el evento ItemCheck. Desactivar y Activar hace que el período de intervalo vuelva a iniciarse.

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 
+0

Te lo puedo compartir. Por otro lado, quizás puedas aprender mejores soluciones de otras respuestas. Usar el temporizador es relativamente complicado y en este caso es una herramienta incorrecta para el trabajo, porque en realidad ya está obteniendo nuevos valores como parámetros. Así que puede usar [esta respuesta] (http://stackoverflow.com/a/17511730/2392157) para uno- de solución o [este] (http://stackoverflow.com/a/18487539/2392157) para una solución sistemática. Convierta de C# a VB usando una de las herramientas de conversión en línea. – miroxlav

1

No sé si esto es aplicable, pero quería utilizar un checklistbox para filtrar los resultados. Entonces, a medida que el usuario marcó y desmarcó los elementos, quise que la lista mostrara \ ocultar 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 todavía funciona sin

El evento que utilizo es "SelectedIndexChanged"

la enumeración que utilizo es ".CheckedItems"

Esto da los resultados que creo que podemos esperar. Así 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 
     } 
    } 

} 
0

en la conducta normal, cuando comprobamos un artículo, el estado del artículo del cheque va a cambiar antes de subir controlador de eventos. Pero CheckListBox funciona con un comportamiento diferente: El controlador de eventos aumenta antes de comprobar el estado del cambio de elemento y dificulta la corrección de nuestros trabajos.

En mi opinión, para resolver este problema, debemos diferir 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());} 
1

Suponiendo que se desea conservar los argumentos de ItemCheck pero ser notificado después de que el modelo se cambió que debe ser similar que:

CheckedListBox ctrl = new CheckedListBox(); 
ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e))); 

Dónde CheckedItemsChanged podrían ser:

private void CheckedItemsChanged(object sender, EventArgs e) 
{ 
    DoYourThing(); 
} 
Cuestiones relacionadas