Ejemplos en Visual Basic

Cómo sincronizar el contenido de dos ListBox

 

Fecha: 16/Abr/1998


Lo que vamos a ver es algo más que un truco, pero no llega a utilidad, así que lo incluyo en esta "sección" de ejemplos o consejos de cómo hacer las cosas con los controles del Visual Basic.

No es nada del otro mundo el efecto que se consigue, pero puede haber circunstancias en las que nos pueden servir.
Sin ir más lejos en el programilla ese que tengo de los códigos postales (
ver sección Gratisware), se podría aplicar esto que voy a explicar para mantener sincronizados las listas de las poblaciones con la de los códigos postales.

Las pruebas que he realizado han sido con listbox que no está clasificados, ya que con estos la cosa se complica más de la cuenta y aunque se puede hacer, no me he puesto en ello.
Las posibilidades que he tenido en cuenta son las siguientes:

  1. Dos listbox sin "multiselección"
  2. Un listbox multiselect y otro normal
  3. Dos listbox multiselect

Realmente he creado unas rutinas (o procedimientos) que se encargan del trabajo, aunque sincronizar el contenido de dos ListBox normales, es decir que no permitan selección múltiple, es relativamente fácil, vamos a ver el código para hacerlo antes de pasar a la parte algo más engorrosa de los que permiten múltiple selección.

Private Sub List1_Click()
    Static YaEstoy As Boolean
    
    'Sincronizar el List1 con el List2
    If Not YaEstoy Then
        YaEstoy = True
        List2.ListIndex = List1.ListIndex
        List2.TopIndex = List1.TopIndex
        YaEstoy = False
    End If
End Sub


Private Sub List2_Click()
    Static YaEstoy As Boolean
    
    'Sincronizar el List2 con el List1
    If Not YaEstoy Then
        YaEstoy = True
        List1.ListIndex = List2.ListIndex
        List1.TopIndex = List2.TopIndex
        YaEstoy = False
    End If
End Sub


Private Sub List1_Scroll()
    'Sincronizar también el primer item mostrado en la lista
    List2.TopIndex = List1.TopIndex
End Sub


Private Sub List2_Scroll()
    'Sincronizar también el primer item mostrado en la lista
    List1.TopIndex = List2.TopIndex
End Sub

Como comprobarás, el código a usar en cada uno de los ListBox es el mismo, lo que cambia es el "orden" en que se sitúan los List1 y List2.
El truco está en que al pulsar en un elemento, se asigne en el otro ListBox el mismo elemento seleccionado.
Además de seleccionar el mismo elemento, se asigna la propiedad TopIndex para que se muestre en la parte superior el mismo elemento en ambos ListBox.
Por otro lado se comprueba también el evento Scroll, éste evento se produce cada vez que se mueve la barra de desplazamiento, por tanto aquí también se asignan en el otro ListBox el elemento que está arriba.

Ni que decir tiene que los dos listbox deben tener el mismo número de elementos, si no es así, no tiene sentido la sincronización.

Vamos a ver lo que habría que hacer si alguno de los dos ListBox tiene asignada la propiedad MultiSelect a algún valor diferente de Ninguno.

Private Sub List3_Click()
    'Sincronizar el List4 con el List3
    SincListBox List3, List4
End Sub


Private Sub List4_Click()
    'Sincronizar el List3 con el List4
    SincListBox List4, List3
End Sub


Private Sub List3_Scroll()
    'Sincronizar también el primer item mostrado en la lista
    List4.TopIndex = List3.TopIndex
End Sub


Private Sub List4_Scroll()
    'Sincronizar también el primer item mostrado en la lista
    List3.TopIndex = List4.TopIndex
End Sub

Como ves el código a usar en el evento Scroll es el mismo que para los ListBox normales, pero para sincronizar el que se haga Click en uno de los ListBox, usamos un procedimiento que se encargará de sincronizar y seleccionar los mismos elementos en ambas listas.
Además de este procedimiento, se usan otros dos, para quitar la selección de un ListBox y otro que se encargará de seleccionar en un ListBox los mismos elementos que haya en otro.
Pero mejor vemos el código:

'
Private Sub SincListBox(elListOrig As Control, elListDest As Control)
    Static EnListBox As Boolean
        
    'Sincronizar el elListDest con el elListOrig
    If Not EnListBox Then
    
        EnListBox = True
        
        'Desmarcar los elementos seleccionados
        QuitarListSelected elListDest
        
        'Marcar en el 1º ListBox los seleccionados del 2º
        PonerListSelected elListOrig, elListDest
        
        'Posicionar el elemento superior
        elListDest.TopIndex = elListOrig.TopIndex
        
        EnListBox = False
    End If
End Sub


Private Sub QuitarListSelected(unList As Control)
    'Quitar los elementos seleccionados del listbox indicado
    'Parámetros:
    '   unList      el List a controlar
    '
    Dim i&
    
    With unList
        'Sólo hacer el bucle si permite multiselección
        If .MultiSelect Then
            For i = 0 To .ListCount - 1
                .Selected(i) = False
            Next
        End If
    End With
End Sub


Private Sub PonerListSelected(elListOrig As Control, elListDest As Control)
    'Marca en el ListDest los elementos seleccionados del ListOrig
    '
    'Los dos listbox deben tener el mismo número de elementos
    '
    Dim i&
    
    'Por si no tienen los mismos elementos
    On Local Error Resume Next
    
    With elListOrig
        For i = 0 To .ListCount - 1
            elListDest.Selected(i) = .Selected(i)
        Next
    End With
        
    Err = 0
End Sub

Realmente el procedimiento que hace el "trabajo duro" es PonerListSelected y este procedimiento vale igualmente para ListBox que puedan seleccionar varios elementos como los que sólo permiten que se seleccione uno sólo, aunque si los dos listbox no permiten múltiple selección es más corto usar el código mostrado al principìo, ya que no hay que hacer ningún bucle.

Por otra parte, se podrían unir los dos procedimientos QuitarListSelected y PonerListSelected, sobre todo si van a tener muchos elementos, así nos ahorramos hacer dos bucles, esto es lo que habría que hacer:

Private Sub ListSelected(elListOrig As Control, elListDest As Control)
    'Marca en el ListDest los elementos seleccionados del ListOrig
    '
    'Los dos listbox deben tener el mismo número de elementos
    '
    Dim i&
    
    'Por si no tienen los mismos elementos
    On Local Error Resume Next
    
    With elListOrig
        For i = 0 To .ListCount - 1
            'Si el origen está seleccionado...
            If .Selected(i) Then
                elListDest.Selected(i) = .Selected(i)
            Else
                'sino, quitar la posible selección
                elListDest.Selected(i) = False
            End If
        Next
    End With
        
    Err = 0
End Sub

Para usarlo sólo habría que cambiar esto en el SincListBox, mejor dicho: así quedaría esta subrutina:

Private Sub SincListBox(elListOrig As Control, elListDest As Control)
    Static EnListBox As Boolean
        
    'Sincronizar el elListDest con el elListOrig
    If Not EnListBox Then
    
        EnListBox = True
        
        'Poner en el ListDest los mismos que en ListOrig
        ListSelected elListOrig, elListDest

        'Posicionar el elemento superior
        elListDest.TopIndex = elListOrig.TopIndex
        
        EnListBox = False
    End If
End Sub

Espero que te pueda ser de utilidad. Pronto pondré otros truquillos para usar con los listbox, por ejemplo: mover un elemento de una posición a otra o crear una serie de rutinas para copiar los elementos seleccionados de un listbox y poder pegarlos en otro, además de eliminar los seleccionados, etc. Todo ello usando PopUp menús y esas chorradillas... en cuanto los "desligue" de la utilidad en la que lo tengo y lo ponga por sí sólos...


Si quieres ver los listados para probar esto, pulsa en este link (sincListBox.zip 2.45 KB)

la Luna del Guille o... el Guille que está en la Luna... tanto monta...