Hacer referencia a controles de multipage, page o frame

11/11/2006 - 04:32 por Ivan | Informe spam
Hola a todos

¿sabeis si existe alguna forma de hacer referencia a los controles
concretos contenidos en un multipagina, pagina y/o frame?

hasta ahora consgo listar todos los controles del formulario (incluso
los 'page', aunque los catalogue -con TypeName - como multipage), pero
no consigo que me de los contenidos en una pagina concreta ( aunque con
el frame y el mutipage no lo he probado, supongo que ocurrira lo
mismo).

he usado la prop. tag para crear una especie de indice y asi me apaño,
pero no se si habra algo mas sencillo

si podeis echarme una mano, oss lo agradezco

un saludo y hasta pronto
Ivan

Preguntas similare

Leer las respuestas

#1 Vinchenzo vinç
11/11/2006 - 12:55 | Informe spam
"Ivan" escribió en el mensaje news:
Hola a todos

¿sabeis si existe alguna forma de hacer referencia a los controles
concretos contenidos en un multipagina, pagina y/o frame?

hasta ahora consgo listar todos los controles del formulario (incluso
los 'page', aunque los catalogue -con TypeName - como multipage), pero
no consigo que me de los contenidos en una pagina concreta ( aunque con
el frame y el mutipage no lo he probado, supongo que ocurrira lo
mismo).

he usado la prop. tag para crear una especie de indice y asi me apaño,
pero no se si habra algo mas sencillo

si podeis echarme una mano, oss lo agradezco

un saludo y hasta pronto




Hola Iván,
si quieres obtener los controles contenidos en una página concreta de un control 'MultiPage', o de un 'Frame', o de cualquier control que pueda actuar como contenedor de controles, tan sólo tienes que recorrer la colección de controles del formulario, comparando la propiedad '.Parent' del objeto de cada iteración, mediante el operador 'Is', con el objeto del que quieres obtener la información.

La siguiente función debe recibir un objeto 'Control', y te devolverá una colección con los controles de primer nivel contenidos:

'********************
Public Function CtlsContenidos(ByRef objContainer As Control) As Collection
Dim oCtl As Control
Set CtlsContenidos = New Collection

For Each oCtl In Controls
If oCtl.Parent Is objContainer Then CtlsContenidos.Add oCtl
Next
End Function
'********************

Y las llamadas són tan simples como:
'==Dim oCtl As Control
Dim colCtls As Collection

'Obtenemos los controles contenidos en la página activa del MultiPage1:
Set colCtls = CtlsContenidos(Me.MultiPage1.Pages(Me.MultiPage1.Value)

Debug.Print colCtls.Count & " controles contenidos"
For Each oCtl In colCtls
Debug.Print vbTab & oCtl.Name
Next
'==

Saludos
( ! ) Respuestas precedentes en Google:
http://groups.google.com/group/micr...c.es.excel
( i ) Temperancia en el foro:
http://support.microsoft.com/defaul...newsreglas
Respuesta Responder a este mensaje
#2 Ivan
11/11/2006 - 19:27 | Informe spam
hola Vinchenzo, lo primero, muchas gracias de nuevo

tu propuesta es exactamente lo que buscaba. La idea es poder listar
todos los controles de todos los formularios de un proyecto, para poder
sanearlos/homojeneizarlos, pues tengo un caos considerable y
probablemente haya 'demasiadas' formas de hacer lo mismo en diferentes
forms/modulos, y supongo que, con lo que voy aprendiendo poco a poco (y
gracias a vosotros), ahora podre simplificarlo bastante.

Bueno, disculpa el rollo, y a continuacion expongo los codigos en los
que he aplicado tu propuesta, por si ves y te parece oportuno
correcciones/simplificaciones (como veras es bastante largo), y por si
pudiera serle util a alguien. Estan diseñados para buscar y nombrar en
5 niveles de 'contenedor' y de momento parecen funcionar bien:->

Niveles => 1º) Formulario -> 2)Multipagina -> 3)Pagina -> 4)Frame ->
5)Frame dentro de otro Frame

'permiteme copiar tu forma de separar los codigos(***), lo hace mas
comodo visualmente
***********************************************************************
' recorre los 5 niveles y va poniendo en una hoja determinadas prop.
de los controles, _
' con los controles 'contenidos' a continuacion de los 'contenedores'
Sub Prueba_Vinch4()
Dim Ctl As Object
Dim oCtl As Control, frCtl As Control, subFrCtl As Control
Dim colCtls As Collection, colCtlsFr As Collection, colCtlsSubFr As
Collection
Dim fiCtl As Integer, nPg As Byte
fiCtl = 1
With ThisWorkbook.Worksheets("Hoja3")
.[a:h].ClearContents
.[a1:h1] = Array("Name", "Caption", "TabIndex", "ControlTipText", _
"BackColor", "TypeName", "Accelerator", "CodeName")
Me.SetDefaultTabOrder
For Each Ctl In Me.Controls
If Esta_Ctl("Hoja3", fiCtl, Ctl) = False Then
fiCtl = fiCtl + 1
Call Prop_Ctl("Hoja3", fiCtl, Ctl)
End If
If TypeName(Ctl) = "MultiPage" Then
For nPg = 0 To Ctl.Pages.Count - 1
If Esta_Ctl("Hoja3", fiCtl, Ctl.Pages(nPg)) = False Then
fiCtl = fiCtl + 1
Call Prop_Ctl("Hoja3", fiCtl, Ctl.Pages(nPg))
End If
Set colCtls = CtlsContenidos(Ctl.Pages(nPg))
For Each oCtl In colCtls
If Esta_Ctl("Hoja3", fiCtl, oCtl) = False Then
fiCtl = fiCtl + 1
Call Prop_Ctl("Hoja3", fiCtl, oCtl)
End If
If TypeName(oCtl) = "Frame" Then
Set colCtlsFr = CtlsContenidos(oCtl)
For Each frCtl In colCtlsFr
If Esta_Ctl("Hoja3", fiCtl, frCtl) = False Then
fiCtl = fiCtl + 1
Call Prop_Ctl("Hoja3", fiCtl, frCtl)
End If
If TypeName(frCtl) = "Frame" Then
Set colCtlsSubFr = CtlsContenidos(frCtl)
For Each subFrCtl In colCtlsSubFr
If Esta_Ctl("Hoja3", fiCtl, subFrCtl) = False Then
fiCtl = fiCtl + 1
Call Prop_Ctl("Hoja3", fiCtl, subFrCtl)
End If
Next
End If
Next
End If
Next
Next
End If
Next
.Columns.AutoFit
.Rows.AutoFit
End With
Set colCtls = Nothing
Set colCtlsFr = Nothing
Set colCtlsSubFr = Nothing
End Sub

******************************************************************
' solo para ahorrar espacio repetitivo
Public Sub Prop_Ctl(ByVal hj As String, _
ByVal fCtl As Integer, ByVal nmCtl As Object)
With ThisWorkbook.Worksheets(hj)
On Error Resume Next
.Range("a" & fCtl) = nmCtl.Name
.Range("b" & fCtl) = nmCtl.Caption
.Range("c" & fCtl) = nmCtl.TabIndex
.Range("d" & fCtl) = nmCtl.ControlTipText
.Range("e" & fCtl) = nmCtl.BackColor
.Range("f" & fCtl) = TypeName(nmCtl)
.Range("g" & fCtl) = nmCtl.Accelerator
.Range("h" & fCtl) = nmCtl.CodeName
On Error GoTo 0
End With
End Sub

***************************************************************************
' para evitar que copie dos veces el mismo control
Public Function Esta_Ctl(ByVal hj As String, ByVal fCtl As Integer, _
ByVal nmCtl As Object) As Boolean
Dim celCtl As Range
With ThisWorkbook.Worksheets(hj)
For Each celCtl In .Range("a1:a" & fCtl)
If celCtl = nmCtl.Name Then
Esta_Ctl = True: Exit Function
End If
Next
End With
Esta_Ctl = False
End Function

****************************************************************************************

Public Function CtlsContenidos(ByRef objContainer As Control) As
Collection
Dim oCtl As Control
Set CtlsContenidos = New Collection
For Each oCtl In Controls
If oCtl.Parent Is objContainer Then CtlsContenidos.Add oCtl
Next
' Set CtlsContenidos = Nothing
End Function

*******************************************************************************************
muchas gracias de nuevo
un saludo y hasta pronto
Ivan

como siempre PD: el eliminar la coleccion creada supongo que en la
funcion no ha lugar, pues la eliminarias antes de utilizarla, pero en
los otros casos creo entender que es conveniente ¿me podrias confirmar
si es asi?, y en cuanto al paso por ByRef de los controles a tu funcion
¿me podrias explicar los motivos?,
espero no estar abusando, y en cualquier caso gracias.
Respuesta Responder a este mensaje
#3 Vinchenzo vinç
13/11/2006 - 21:33 | Informe spam
"Ivan" <@> escribió en el mensaje news:

...
La idea es poder listar todos los controles de todos los formularios de un proyecto,
...



Hola,
existe la colección 'UserForms', pero sólo contiene los formularios cargados, así que para recorrer la colección de formularios que pertenecen al proyecto, necesitarás:
1) Activar la opción del libro:
Herramientas
Macro
Seguridad
Fuentes de confianza
Confiar en el acceso a proyectos de Visual Basic
2) Añadir la referencia desde el IDE de VBA a la librería:
Herramientas
Referencias
Microsoft Visual Basic for Applications Extensibility

Y puedes usar una función que te devuelva una colección con los formularios presentes en el proyecto, que podría ser así:
'*****************
Public Function Forms() As Collection
Dim oCmp As VBComponent

Set Forms = New Collection
For Each oCmp In ThisWorkbook.VBProject.VBComponents
If oCmp.Type = vbext_ct_MSForm Then
Forms.Add oCmp, oCmp.Name
End If
Next
End Function
'*****************

Para recorrerla, tendrías que hacer algo como:
'Dim oForm As VBComponent
Dim oUserForm As UserForm
Dim oCtl As Control

For Each oForm In Forms
Set oUserForm = GetForm(oForm.Name)
If oUserForm Is Nothing Then _
Set oUserForm = UserForms.Add(oForm.Name)

For Each oCtl In oUserForm.Controls
...
Next
Next
'
Una pregunta, he probado el ejemplo que has enviado, y creo que -visualmente- el resultado no te permitirá ver la jerarquía entre controles, y aunque de hecho puedes indentar los textos, pregunto, ¿has pensado en usar un control 'TreeView' para mostrarlos?.
De hecho, no sé muy bien qué aconsejarte, porque no sé cuál es el objetivo real (la información que buscas depurar), es decir, que no entiendo muy bien lo de «para poder sanearlos/homojeneizarlos». Pero seguro que la solución pasará por diseñar una función recursiva, que te servirá para cualquier nivel de anidación de controles, en lugar de los cinco niveles estáticos que acepta tu propuesta.
A ver si con un poco más de tiempo, entre mañana y pasado, te puedo preparar un pequeño ejemplo.


.Range("h" & fCtl) = nmCtl.CodeName



Por lo que tengo entendido, 'CodeName' no se aplica a los controles. En teoría, de esa línea no obendrás ningún resultado (sólo un error en cada iteración), pues se aplica a objetos como 'Worksheet', 'Workbook', etc.
Digamos que la propiedad '.Name' de una hoja se correspondería con el texto de la pestaña de la hoja, en su hoja de propiedades sería la "Name", en cambio '.CodeName' sería la propiedad "(Name)", de lectura en tiempo de ejecución.


Public Function CtlsContenidos(ByRef objContainer As Control) As Collection
...
' Set CtlsContenidos = Nothing
End Function

eliminar la coleccion creada supongo que en la funcion no ha lugar, pues la eliminarias antes de utilizarla



Así es, la colección es la devolución de la función, por lo que al destruirla desde la propia función, el procedimiento llamante siempre obtendría una referencia a un objeto no inicializado al regresar de la llamada.


pero en los otros casos creo entender que es conveniente ¿me podrias confirmar si es asi?



No tanto conveniente, sino recomendable, o mejor dicho, buena práctica. Las variables se destruyen automáticamente al salir de su módulo o procedimiento de alcance, pero tampoco se pierde nada por realizar la destrucción implícitamente.


, y en cuanto al paso por ByRef de los controles a tu funcion ¿me podrias explicar los motivos?,



Es una costubre que tengo cuando debo acceder a los miembros de un objeto, paso el objeto por referencia. Y los valores, por valor, cuando no voy a necesitar modificarlos. Sabrás que en Visual Basic tienes dos formas de recibir los argumentos en una función o procedimiento, ByRef (por referencia), que es el tipo predeterminado, pasa el objeto (su dirección propiamente), en cambio ByVal (por valor) pasa una copia.

Para el acceso al objeto pasado, acabas trabajando sobre él, pero no directamente cuando lo has pasado por valor.
Para que lo veas con un ejemplo, en el siguiente código verás dos funciones, "ObjPtr" y "VarPtr". La primera devuelve la dirección de memoria de la interfaz referenciada por una variable de objeto, y la segunda, la dirección de una variable.
En este ejemplo referenciaremos un objeto 'WorkSheet', y como podrás observar, 'ObjPtr(Objeto)' devuelve en todos los casos la misma dirección, en cambio la dirección de 'VarPtr(Objeto)' sólo coincide con la de la hoja cuando ha sido pasado por referencia:

'************
Sub EvaluarObj()
Debug.Print "ObjPtr(Sheets(1)): " & ObjPtr(Sheets(1))
Debug.Print "VarPtr(Sheets(1)): " & VarPtr(Sheets(1))
Call ObjByRef(Sheets(1))
Call ObjByVal(Sheets(1))
End Sub

Sub ObjByRef(ByRef Objeto As Worksheet)
Debug.Print "ObjPtr(ByRef Objeto): " & ObjPtr(Objeto)
Debug.Print "VarPtr(ByRef Objeto): " & VarPtr(Objeto)
End Sub

Sub ObjByVal(ByVal Objeto As Worksheet)
Debug.Print "ObjPtr(ByVal Objeto): " & ObjPtr(Objeto)
Debug.Print "VarPtr(ByVal Objeto): " & VarPtr(Objeto)
End Sub
'************


Saludos
( ! ) Respuestas precedentes en Google:
http://groups.google.com/group/micr...c.es.excel
( i ) Temperancia en el foro:
http://support.microsoft.com/defaul...newsreglas
Respuesta Responder a este mensaje
#4 Ivan
15/11/2006 - 04:23 | Informe spam
hola Vinchenzo, (aviso: es un autentico tomo)

lo primero, aparte de por supuesto agradecerte nuevamente tu ayuda,
disculparme por la tardanza en la respuesta. Entre que ando 'liadillo',
y que no queria responderte sin al menos haber tanteado(investigado) un
poco tus propuestas y consejos, no he podido hacerlo antes.

La verdad es que no he tenido mucho exito con mis pruebas y
'busquedas', pero tus explicaciones (tanto estas, como las que me has
proporcionado en otras ocasiones) me estan siendo inmensamente utiles
para ir comprendiendo (por desgracia muy poco a poco, soy duro de
mollera) muchos de los conceptos que tengo atravesados, y que, como la
funcion que mencionas, son recursivos y me hacen atorarme
continuamente.

Bueno, despues de mi rollo de rigor, paso a comentarte tu respuesta y
algunos de los avatares en los que me he metido. Y, por si tienes
tiempo, y te parece procedente, vuelvo a abusar de tu generosidad con
algunas consultas:

Hola,
existe la colección 'UserForms', pero sólo contiene los formularios cargados, así que para recorrer la colección de formularios que pertenecen al proyecto, necesitarás:..

2) Añadir la referencia desde el IDE de VBA a la librería:
Herramientas
Referencias
Microsoft Visual Basic for Applications Extensibility



esto explica(creo), al menos en parte, la inutilidad de mis intentos de
acceder a la coleccion UserForms.

¿Sabes si hay en la ayuda (o en ...) explicaciones un poco mas
'explicitas' sobre los UserForms, y a ser posible el acceso a
VBComponents, que a las que se accede (al menos en mi caso) con F1
(Coleccion UserForms, Ventana UserForms, y poco mas)?

En cuanto al uso/habilitacion de Referencias, tengo un autentico
cacao a causa precisamente de intentar encontrar ayuda para casos como
el anterior y otros muchos, como veras un poco mas adelante. Ahora
mismo tengo unas cuantas habilitadas, que posiblemente solo me esten
lastrando el ordenador, pero que ya no se muy bien cuando las cargue y
ahora no me atrevo a deshabilitarlas.

La pregunta seria basicamente la misma ¿donde podria encontrar
ayuda sobre el tema, sino detallada, si al menos un poco mas clara y
extensa que la del propio excel ? o en su defecto ¿cuales serian las
(practicamente) imprescindibles para un uso 'normal' de excel?. Seria
sobre todo para dejar las minimas y cargar solo las especificas para
determinados casos, como el que propones.


Y puedes usar una función que te devuelva una colección con los formularios presentes en el proyecto, que podría ser así:


.
'*****************
Para recorrerla, tendrías que hacer algo como:
'


aunque no he tenido tiempo de probarlo, me da la impresion de que
el sistema es basicamente el mismo que para los controles, como +o-
habia supuesto/intentado, pero claro, accediendo realmente a los
Userforms. Gracias de nuevo, creo que me va a resultar muy util.

Una pregunta, he probado el ejemplo que has enviado, y creo que -visualmente- el resultado no te permitirá ver la jerarquía entre controles,



tienes razon, y es lo que me estaba/esta volviendo loco

y aunque de hecho puedes indentar los textos, pregunto, ¿has pensado en usar un control 'TreeView' para mostrarlos?.



la verdad es que no conocia este control, y despues de echarle un
ojo, creo que efectivamente me podria valer, sobre todo por claridad
visual, pero tambien me interesria que luego se pudieran traspasar a
una hoja, por lo que despues te comento. En cuanto a TreeView, lo he
cargado en el cuadro de herramientas, pero no me permite ver la ayuda
sobre sus propiedades, etc. Me da el error :

"No se puede hallar el archivo comctl1.htp ¿Deseas ?"

Y aqui vuelven las dichosas referencias, he cargado/descargado no
se cuantas (entre ellas 'Windows Common ' no recuerdo que mas, pero a
la que se hacia referencia en una pagina de Internet, hablando del
mismo control., y nada). Tambien hace poco desinstale una demo de un
programa de TPV basado (creo) en visual fox pro y creo recordar alguna
mencion a dicho archivo en alguna ocasion. ¿cres que puedo haber
desinstalado el archivo junto con el TPV?¿como podria
arreglarlo?¿reinstalando el office?

De hecho, no sé muy bien qué aconsejarte, porque no sé cuál es el objetivo real (la información que buscas depurar), es decir, que no entiendo muy bien lo de «para poder sanearlos/homojeneizarlos».



no se si sabre explicar mis intenciones, por otro lado bastante
difusas, pero lo intentare:

aunque el motivo ultimo, y quizas principal, es aprender algo de
VBA (confieso que por puro vicio), en este caso se trata de un proyecto
(una especie de biblioteca, con fichas, ficheros, busquedas por muchos
campos, presentacion con imagenes y musica, contacto entre usuarios,
actualizaciones, modificaciones restringidas por usuario, impression de
fichas y -posiblemente- configuracion de las mismas por el usuario, una
interface casi totalmente personalizada, y no se que mas) que
comence hace +o- 7 meses, cuando apenas tenia ni idea de VBA, y en la
cual he ido acumulando practicamente toda mi evolucion en estas lides
aparte de haberlo ido enrevesando cada vez mas en cuanto a prestaciones
se refiere,

con lo que al final me he encontrado con un monton de formularios,
modulos, hojas, y por supuesto codigos y variables., que en muchos
casos seguramente podria simplificar bastante y/o unificar mediante
(pej) argumentos en el caso de los codigos, y (pej) multipaginas en el
caso de los formularios,

otro motivo es que estoy usando para la mayoria de los codigos
'importantes' una hoja (bueno, todavia varias) del libro con infinidad
de listas a modo de matrices, en las cuales tengo otro cacao
considerable, pero que al verse afectadas por muchos codigos de muy
variados modulos y formularios, resulta un autentico engorro
simplificar.

Por eso (y tambien como te digo por practicar) estoy intentando hacer
unos listados con los controles, asi como lo estoy intentando con
variables y procedimientos, para intentar 'minimizar' el codigo, los
objetos, etc., pero sin empezar de nuevo, sobre todo por que me temo
que comenzaria a complicarme la vida otra vez con mas añadidos y
podria no acabar nunca.

como veras he vuelto a soltar otro de mis rollos

Pero seguro que la solución pasará por diseñar una función recursiva, que te servirá para cualquier nivel de anidación de controles, en lugar de los cinco niveles >estáticos que acepta tu propuesta.



llevo tiempo intrigado con el funcionamiento de las funciones
recursivas, aunque todavia no he intentado probar ninguna. Quizas vaya
siendo hora

A ver si con un poco más de tiempo, entre mañana y pasado, te puedo preparar un pequeño ejemplo.



cualquier ayuda te la agradezco, aun mas si va acompañada de tus
magnificas explicaciones (y te aseguro que no exagero en el adjetivo),
pero en cualquier caso tu ayuda ya es impagable (otra vez me repito,
pero no miento, ni en tu caso, ni en el de la mayoria de los demas
expertos(y no espertos) del foro)

Por lo que tengo entendido, 'CodeName' no se aplica a los controles. En teoría, de esa línea no obendrás ningún resultado (sólo un error en cada iteración), pues se aplica a objetos como 'Worksheet', 'Workbook', etc.



tienes razon, ya me habia dado cuenta, Pensaba que tambien los
controles tenian CodeName (en realidad pensaba que todos los objetos,
tendre que 'revisarme') .
No tanto conveniente, sino recomendable, o mejor dicho, buena práctica. Las variables se destruyen automáticamente al salir de su módulo o procedimiento de alcance, pero tampoco se pierde nada por realizar la destrucción implícitamente.



gracias de nuevo

> , y en cuanto al paso por ByRef de los controles a tu funcion ¿me podrias explicar los motivos?,



esta es otra de mis espinas. Aunque creo entender las
defiiciones/explicaciones sobre ByRef/ByVal que voy encontrando, no
consigo acabar de 'comprender' su, llamemosle, uso practico, o sea, las
consecuencias al usar uno u otro y la diferencia en el resultado,
llamemosle, 'real' .

pero me temo que seria un tema demasiado extenso y creo que ya he
sobrepasado con creces lo politcamente correcto, en lo que a extension
y diversificacion de consultas se refiere.

Aunque como ya no creo que se note mucho, te hago una ultima:

Debug.Print "ObjPtr(ByVal Objeto): " & ObjPtr(Objeto)



no se que me pasa en la ventana Inmediato que no consigo que
funcione como las otras del editor, o me ocupa toda la pantalla, o se
minimiza, o funciona como una ventana de modulo mas. No consigo que se
quede acoplada abajo o donde sea, como las demas (inspeccion, etc), y
como, si no me equivoco, ella misma hasta hace poco. Cuando ocupa toda
la pantalla,solo tiene el boton de cerrar(x), ni maximizar ni
minimizar. He revisado opciones pero no he visto nada.

Esta si es la vencida. Espero no haberte aburrido demsiado y en
cualquier caso gracias por todo

Un saludo y hasta pronto
Ivan
Respuesta Responder a este mensaje
#5 Ivan
15/11/2006 - 18:57 | Informe spam
Hola de nuevo,

se me olvidaba otro motivo que, aunque practicamente ya lo he
descartado (porque creo que implicaria usar modulos de clase, y de
momento no tengo ni la mas remota idea de manejarlos), tambien era de
los principales para listar los controles:

se trataba de crear los formularios de forma dinamica en tiempo de
ejecucion, un poco como las barras de menus y herramientas, para los
que utilizo una lista/matriz de una hoja con sus respectivas
propiedades. Pero, aparte de que supongo que ralentizaria algo la
ejecucion, estan los dichosos modulos de clase, a los que tengo un
considerable respeto.

¿quizas existe alguna forma ''''sencilla'''' de hacerlo?

un saludo y hasta pronto
Ivan
Respuesta Responder a este mensaje
Ads by Google
Help Hacer una preguntaSiguiente Respuesta Tengo una respuesta
Search Busqueda sugerida