Saltar al contenido

Aporte al foro: Lista desplegable inteligente.


YoelMonsalve

Recommended Posts

Buenos días comunidad, estoy pensando además de responder las consultas por aquí presentadas, en escribir posts sobre temas diversos pero útiles que por ejemplo a mí me han surgido en trabajos realizados con anterioridad. Esperando dichos recursos puedan serle útiles a alguien más o incluso profundizar en el estudio de ellos (hay bastantes en VBA).

En esta ocasión quiero presentar un ListBox (o más precisamente DropDown) que se rellena "inteligentemente". El problema es básicamente el siguiente: tenemos un conjunto de datos en una hoja de Excel, y queremos que al escribir en una celda de otra hoja, los datos puedan ser seleccionados de entre el conjunto definido en la primera hoja, y por medio de un DropDown, o lista desplegable.

Nota: En este punto quiero aclarar que en lo persona aún me queda clara la diferencia entre controles "DropDown" y "ListBox" de Microsoft, lo que puedo ver es que ListBox es el nombre que reciben cuando son insertados en objetos Userform (formularios), y DropDown cuando se insertan en la hoja de cálculo. Además aclaro a los lectores que no utilicé controls ActiveX porque tampoco tengo totalmente claro sus ventajas con respecto a los controles de formulario tradicionales, o qué ventaja puedan tener los ActiveX sobre estos.

Siguiendo con el tema, una opción sería asociar un rango fijo o estático de datos con el control DropDown pero esta no es la solución satisfactoria si el rango crece o decrece, o cambia completamente como veremos en ampliaciones posteriores de este mismo trabajo. Entonces podemos mediante el uso de macros cargar automáticamente la lista desplegable con datos en forma dinámica. Concretamente, suponga que tenemos un conjunto de valores en la columna A de Hoja1, y queremos que estos sean mostrados como opciones en el DropDown de la Hoja2. Adicionalmente, al clicar en el DropDown, el valor elegido sea transferido a la celda F3 de Hoja 2 (ver el ejemplo del archivo adjunto).

El primer paso sería escribir los datos en la Hoja1, y en la Hoja2 crear un DropDown en este caso llamado "Lst_Nombres". Luego necesitamos dos procedimientos, uno SET_Lst y otro LOAD_Lst. El primero recibe como argumento un objeto de la clase DropDown y se utiliza como para crear una referencia a la lista desplegable que queremos. Básicamente busca entre todos los DropDowns de la hoja activa uno cuyo nombre sea "Lst_Nombres", y si lo encuentra usa una instrucción "Set" de Visual Basic para asociar el argumento recibido con el objeto encontrado. Si no encuentra un DropDown con dicho nombre, entonces asigna Nothing al parámetro pasado.

Sub SET_Lst(ByRef Lst As DropDown)
	    Dim i As Integer, _
        N As Integer
	    Dim LstName As String
    Dim LstExists As Boolean
        
    ' buscando el objeto con el nombre dado, y si lo encuentra lo
    ' asocia a Lst
    LstName = "Lst_Nombres"
    LstExists = False
    For i = 1 To ActiveSheet.DropDowns.Count
        If LCase(ActiveSheet.DropDowns(i).Name) = LCase(LstName) Then
            Set Lst = ActiveSheet.DropDowns(i)
            LstExists = True
            Exit For
        End If
    Next
    If LstExists = False Then
        Set Lst = Nothing
        Exit Sub
    End If
	End Sub
	

Este proceso puede parecer incómodo pero es necesario porque más adelante haremos un llamado a nuestro objeto de lista desplegable desde un módulo de código externo, concretamente del evento Worksheet_Activate(). Entonces, este otro proceso necesita referenciar el objeto DropDown apropiado (la otra otra alternativa sería declarar variables globales con visibilidad desde todos los módulos del proyecto de VBA, pero como se sabe todo enfoque basado en variables globales es básicamente una idea riesgosa).

El otro procedimiento es el que carga los valores en la lista desplegable, y recibe de argumento un objeto DropDown (obtenido justamente con el SET_Lst anterior) cuyos elementos serán rellenados dinámicamente, o sea, en cuánto sea invocado el procedimiento.

	Sub LOAD_Lst(Lst As DropDown)
	    Dim SheetName As String
    Dim S2 As Worksheet
    Dim i As Long, _
        N As Long
    
    ' Nombre de la hoja donde buscar los datos. Puede cambiarlo si es
    ' necesario.
    SheetName = "Hoja1"
    Set S2 = Sheets(SheetName)
    
    ' calculando el número de elementos a cargar
    If S2.[a1] = "" Then
        N = 0
    Else
        If S2.[a2] = "" Then
            N = 1
        Else
            N = S2.[a1].End(xlDown).Row - 1
        End If
    End If
    If N = 0 Then Exit Sub      ' sale, si no hay elementos
    
    ' y rellena la lista
    Lst.RemoveAllItems
    For i = 1 To N
        Lst.AddItem S2.Range("a" & i + 1)
    Next
End Sub

Como se puede ver en los códigos anteriormente mostrados, prefiero declarar objetos Worksheet y asignarles una hoja específica a partir de su nombre, que usar a cada rato instrucciones tan largas como: [tt]Sheets("Hoja1")[/tt]. Si la hoja con dicho nombre no existe, se generará un error en tiempo de ejecución, lo cual se puede corregir pero complicaríamos demasiado el ejemplo (yo sí suelo usar rutinas de gestión de error y comprabación de las variables en todo mi código).

Ahora, si queremos que el ítem seleccionado del DropDown sea pasado a la ceda F3 de Hoja2 simplemente tenemos que añadir un Callback a la lista:

' Callback para del DropDowm.
' Pasa el valor del ítem seleccionado a la celda F3 de "Hoja2"
Sub Lst_Click()
    
    Dim SheetName As String
    Dim S1 As Worksheet
    Dim i As Integer
    
    Dim myLst As DropDown
    
    ' Nombre de la hoja donde buscar los datos. Puede cambiarlo si es
    ' necesario.
    SheetName = "Hoja2"
    Set S1 = Sheets(SheetName)
    
    ' obtiene la lista
    SET_Lst myLst
    
    If Not myLst Is Nothing Then
        i = myLst.ListIndex
        If i > 0 Then
            S1.[f3] = myLst.List(i)
        End If
    End If
    
End Sub

Anteriormente habíamos dicho que si [tt]SET_Lst[/tt] no encuentra el objeto DropDown (porque el usuario lo borró accidentalmente por ejemplo) entones asignará [tt]myLst = Nothing[/tt]. Esta precaución le permite saber al código de [tt]Lst_Click()[/tt] que el myLst no ha sido asignado y por lo tanto no intentará rellenarlo. Aquí vemos otro ejemplo de una buena práctica de programación.

Y finalmente, para que la lista sea rellenadamente automáticamente cada vez que entremos en la Hoja2, hace falta programar el evento [tt]Worksheet_Activate()[/tt] de dicha hoja:

	Private Sub Worksheet_Activate()
	    Dim myLst As DropDown
	    ' fija el objeto myLst de la clase DropDown
    SET_Lst myLst
    
    ' y si tuvo éxito, rellena la lista
    If Not myLst Is Nothing Then
        LOAD_Lst myLst
        ActiveSheet.[f3] = ""
    End If
End Sub

IntelligentList_by_Yoel_v.1.xlsm

Enlace a comentario
Compartir con otras webs

Lo mismo, pero con un control ComboBox ActiveX.

En la hoja Hoja2:

Private Sub Worksheet_Activate()
ComboBox1.List = Sheets("Hoja1").Range("A2:A" & Hoja1.Range("A" & Rows.Count).End(xlUp).Row).Value
End Sub

Private Sub ComboBox1_Change()
[F3] = ""
If ComboBox1.ListIndex > -1 Then [F3] = ComboBox1
End Sub

 

IntelligentList.xlsm

Enlace a comentario
Compartir con otras webs

Muchas gracias marcolios, y la idea es que lo puedas usar este recurso en tus proyectos, si consideras que te puede resultar útil. Y como realmente lleva bastante código, en un post posterior publicaré cómo podemos encapsularlo en una "clase" para mayor comodidad.

Por último, si te resultó interesante, puedes ayudarme con un "me gusta" :)

Enlace a comentario
Compartir con otras webs

Archivado

Este tema está ahora archivado y está cerrado a más respuestas.

  • 96 ¿Te parecen útiles los tips de las funciones? (ver tema completo)

    1. 1. ¿Te parecen útiles los tips de las funciones?


      • No
      • Ni me he fijado en ellos

  • Ayúdanos a mejorar la comunidad

    • Donaciones recibidas este mes: 0.00 EUR
      Objetivo: 130.00 EUR
  • Archivos

  • Estadísticas de descargas

    • Archivos
      177
    • Comentarios
      90
    • Revisiones
      27

  • Crear macros Excel

  • Mensajes

    • buenas noches, quisiera saber si puedo mejorar mi macros que se encuentra en el evento change de la hoja de calculo de Excel, son códigos de cálculos básicos, además si me pudieran ayudar a reducir el código o darme algún tip para reducirlo yo mismo estaría muy agradecido.  de ante mano muchas gracias     Private Sub Worksheet_Change(ByVal Target As Range)     Application.ScreenUpdating = False     Application.Calculation = xlManual     Application.EnableEvents = False              If Not Intersect(Target, Range("$L$5:$Y$9")) Is Nothing Then             Sub todo()          Range("E22") = WorksheetFunction.Sum(Range("E4:E21"))     Range("E23") = WorksheetFunction.Sum(Range("E4:E19"))     Range("E24") = WorksheetFunction.Sum(Range("E23") - WorksheetFunction.Sum(Range("I4:I7")))     Range("i22") = WorksheetFunction.Sum(Range("I4:I21"))     Range("I4") = Range("E23") * 0.1     Range("I6") = Range("E23") * 0.0127     Range("I5") = Range("EN10") * Range("EN11")     Range("I7") = Range("E23") * 0.006     Range("I25") = Range("E22") - Range("I22")     Range("I12") = Range("E24") * 0.03     Range("C7") = WorksheetFunction.Sum(Range("EQ8") - (Range("EQ9"))) + Range("EN13") + Range("EN14") + Range("EN15") + Range("EN16")     'Range("E7") = WorksheetFunction.Sum(Range("C7") * ((Range("E4") * 0.0077777)))     Range("C9") = Range("EQ9") + Range("EN17") + Range("EN18") + Range("EN19")     'Range("E9") = WorksheetFunction.Sum(((((Range("E4") / Range("C4")) * 7) / 45) * 1.3) * 1.5) * Range("C9")     'Range("E8") = WorksheetFunction.Sum(((((Range("E4") / Range("C4")) * 7) / 45) * 0.3)) * (Range("C8"))     Range("E9") = WorksheetFunction.Sum(((((Range("E4") / Range("C4")) * 7) / 44) * 1.3) * 1.5) * Range("C9")     Range("E8") = WorksheetFunction.Sum(((((Range("E4") / Range("C4")) * 7) / 44) * 0.3)) * (Range("C8"))     Range("E7") = WorksheetFunction.Sum((((Range("E4") / Range("C4")) * 7) / 44) * 1.5) * Range("C7")          End Sub              End If                  Application.Calculation = xlAutomatic     Application.ScreenUpdating = True     Application.EnableEvents = True     End Sub
    • Buenas perdonad la espera adjunto el fichero Excel y explico mas detallado lo que me solicitan: Lo que me solicitan es que esos CP de la pestaña Casos de prueba los cuales tienen formulas para que cuando se copien y peguen junto a sus pasos el CP se va autoincrementando a 1,2,3 etc...., pero si copias ese CP bien solo con el primer paso o con todos sus  pasos y lo insertas entre dos CP no continua con la numeración, como se ve en la foto al hacer eso el CP insertado continua con la numeración CP2 y el de abajo pone también CP2 no se incrementa ni ese ni el valor de CU que hay a la derecha que también es incremental. Necesitaría que continuara con esa numeración aun insertándolo entre medio de 2 tanto el CP como el CU de la derecha. También me solicitan que el campo Ciclo 1 de la pestaña Resumen es auto incremental cuando copias y pegas va sumando 1, pero en las formulas referentes a Ok KO Y bloqueo al copiar y pegar se mantiene la misma formula , la idea es que cuando copies y pegues la fila donde están los ciclos se autoincremente Ciclo a 1,2,3,4 etc... y la formula de los campos OK,KO y Bloqueos se incremente también pasando de la columna I a la J  luego a la k etc... y que en Resultados Ciclo el numero de la formula también se incremente Resultados Ciclo 1 , 2 ,3 etc... que cambie la columna a la vez que el numero de Resultados Ciclo. Gracias por adelantado un saludo. Plantilla v3 Pruebas.xlsx  
    • Saludos amigos espero estén bien Estoy intentando hacer un formulario que me convierta unidades de masa sin embargo  en el mejor de los casos solo he podido lograr la conversión de una unidad a la vez en los TextBox 1, 3, 5, 7, 9, 11 y 13 y cuando lo logro el resultado que se copia  en la celda no se corresponde con el obtenido originalmente en el Textbox del Formulario (frmconv)  ejemplo al convertir 1900 Kg a Lb el resultado en el TextBox1 =4188,78298142 sin embargo al guardar el resultado lo que se copia en la Celda  "F11" es  418.878.298.142,00, adicionalmente el resultado de la conversión no se visualiza inmediatamente por lo que debo de hacer click en los TextBox 1, 3, 5, 7, 9, 11 y 13  para ver el resultado. Mucho les sabre agradecer la ayuda que me puedan brindar. PRUEBA.xlsm
    • Saludos a ambos. Copiar y pegar por sí solas, no tengo el conocimiento de que sirvan como "evento" para actualizar las referencias que buscas hacer, en la forma que lo quieres hacer, ó la fórmula como la quieres hacer. Te recomiendo abrir un tema similar en Macros, es posible que algún Maestro te de alguna idea. Por otro lado, si debe ser con funciones, entonces tendías que interactuar con COLUMNA() y FILA() para que al pegar el destino "sepa" donde está ubicado e intentar cambiar la referencia. =INDIRECTO(CARACTER(COLUMNA()+64)&FILA()) Algo como eso se podría usar para obtener el código ASCII de la letra de la columna (donde 65 es el código para “A”), y FILA() devuelve el número de la fila. La función CARACTER() convierte el código ASCII en una letra. Luego, INDIRECTO() toma la cadena resultante (por ejemplo, “A1”, “B2”, etc.) y la usa como una referencia de celda. En ese caso, una posible idea de editar tu ejemplo sería: =SI(INDIRECTO("'Casos de Prueba'!"&CARACTER(COLUMNA()+64)&"1")="Resultados Ciclo 1"; SI(CONTAR.SI(INDIRECTO("'Casos de Prueba'!"&CARACTER(COLUMNA()+64)&":"&CARACTER(COLUMNA()+64)); "OK")=0; 0; CONTAR.SI(INDIRECTO("'Casos de Prueba'!"&CARACTER(COLUMNA()+64)&":"&CARACTER(COLUMNA()+64)); "OK")); 0)   Enfatizo que es una idea, es muy probable que haya que editar. Así como esta su tema, la recomendación del maestro toma relevancia porque especular o deducir no es lo adecuado para intentar ayudar en este tipo de consultas. Por esta causa de mi parte por ejemplo no puedo aportar algo adicional.
    • En el ejemplo te he puesto 1 segundo para no hacer largo el gif, cámbialo a tu necesidad
  • Visualizado recientemente

    • No hay usuarios registrado para ver esta página.
×
×
  • Crear nuevo...

Información importante

Echa un vistazo a nuestra política de cookies para ayudarte a tener una mejor experiencia de navegación. Puedes ajustar aquí la configuración. Pulsa el botón Aceptar, si estás de acuerdo.