¿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
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/microsoft.public.es.excel
( i ) Temperancia en el foro:
http://support.microsoft.com/default.aspx?scid=fh;ES-ES;newsreglas
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.
>...
> 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
'************
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
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
no tengas ningun problema, yo ando mas o menos igual, y ademas, con las
'tareas' de tu ultimo mensaje tengo una buena dosis de trabajo para la
espera. Y poco a poco parece que algunas van dando sus frutos.
pej, en lo que se refiere a ByRef y ByVal ( y despues de tu aclaracion
sobre el espacio/lugar en memoria que tampoco acababa de tener claro
con la ayuda), siguiendo un poco tu ej., he hecho esta sencilla prueba
que mas o menos ha resuelto mis dudas sobre sus efectos 'reales', que
aunque (creo que) son 'exactamente' los que dice la ayuda, no acababa
de verlos
***********************************
Function ProbarVal(ByVal nPi As Integer) As Integer
ProbarVal = nPi * 100
nPi = ProbarVal
End Function
**********************************
Function ProbarRef(ByRef nPi As Integer) As Integer
ProbarRef = nPi * 100
nPi = ProbarRef
End Function
***********************************
Sub Pruebas_Inmediato()
Dim x As Integer
x = 1
Debug.Print ProbarVal(x)
Debug.Print x
x = 1
Debug.Print ProbarRef(x)
Debug.Print x
End Sub
**********************************
en cuanto a los controles sigo en la pelea, pero cuando creo que lo he
logrado, de repente alguno se ha 'escapado', ....o se ha ido a la casa
de un vecino.
y en cuanto a la ventana inmediato, he conseguido 'medio' domarla, y
digo medio porque aunque he conseguido que se comporte mas o menos
normal, ahora es como si hubiera dos instancias de la misma.->ahora
mismo (creo). una minimizada (que me estorba pej. al intentar volver al
editor desde este mensaje, pues se maximizaba y me impedia acceder al
editor hasta que dando un rodeo he conseguido minimizarla) y otra
activa en su posicion normal. Pero bueno no quiero volver a alargarme,
voy a ver por donde anda cuando vuelva.
muchas gracias de nuevo por tu atencion, y estare encantado de recibir
tu ayuda cuando puedas. Mientras voy a ver que es eso de .....
'recursivas'
> ¿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)?
No conozco nada en concreto, yo siempre voy "picoteando de aquí y de allá". Entre Google y los grupos de discusión se suele encontrar mucha más información que en la propia ayuda, pero habitualmente dispersa.
Tal vez otro miembro del grupo conozca y pueda sugerir algún lugar específico.
> En cuanto a ... Referencias ...
> ¿donde podria encontrar
> ayuda sobre el tema, sino detallada, si al menos un poco mas clara y
> extensa que la del propio excel ?
El problema es que las referencias disponibles, que básicamente son el acceso a librerías externas de componentes instalados en el computador, pueden pertenecer a gran variedad de aplicaciones. Por decir algo, si instalas el 'Adobe Reader' (versión 6, creo), podrás agregar un componente suyo (Adobe Acrobat Control for ActiveX) el 'Pdf.ocx'. Lo mismo sucede con muchas otras aplicaciones, y en ese caso no te queda más remedio que intentar encontrar (si está publicada) la documentación del componente.
Por otro lado, unas referencias las añades manualmente, y otras pueden añadirse automáticamente, cuando seleccionas un control adicional. Por ejemplo (aprovechando nuestro caso), supongamos que quieres usar un control TreeView: abres los "Controles adicionales", lo buscas en la lista y lo seleccionas. En este momento te aparece el icono en el cuadro de herramientas. Cuando añadas el control al formulario, verás en las referencias del proyecto que se ha añadido la referencia de la librería a la que pertenece.
En cambio, si quisieras usar (por poner un ejemplo) el modelo de objetos del sistema de ficheros (FileSystemObject), añadirás manualmente la referencia 'ScrRun.dll' (Microsoft Scripting Runtime), pues no expone ninguna interfaz gráfica, sino una colección de propiedades y métodos a la que accederás desde código cuando declares e instancies una variable de su tipo «Dim oFs As New FileSystemObject» (esto se conoce como Early Binding, enlace temprano).
Y de hecho puedes crear referencias en tiempo de ejecución (conocido como Late Binding, enlace tardío), siguiendo el ejemplo, al 'FileSystemObject', declararías una variable genérica 'Object', y la instanciarías tal que: «Set oFs = CreateObject("Scripting.FileSystemObject")».
Sucede lo mismo con los controles adicionales, tanto puedes añadir el control en el formulario en tiempo de diseño, como no añadir su referencia ni control adicional sino crearlo en tiempo de ejecución. La colección 'Controls' dispone del método '.Add', desde el que puedes hacer algo como:
'******
With Controls.Add("Forms.CommandButton.1", "MiBotón", True)
.Top = 10
.Left = 20
.Caption = "Nuevo botón"
End With
'y como está en la colección, puedes acceder a él por su nombre...
Controls("MiBotón").Caption = "Nuevo Caption"
'ó en el caso del TreeView
With Controls.Add("MSComctllib.TreeCtrl", "MiTree", True)
...
'******
Pero habitualmente esto no tiene mucho sentido cuando se trata de controles que requieren interacción por parte del usuario, porque te obligas a declarar una variable con eventos, y codificar dichos eventos de antemano. Que sería: Private WithEvents MiControl As TipoDeControl.
Se consigue reducir el tamaño del fichero, pero nada del otro mundo (dependiendo de la cantidad que fueran, pero igualmente deberían ser muchos para un cambio sustancial), y es un poco liante, pues no puedes crear matrices de controles...
...bueno, creo que me he desviado del tema...
> o en su defecto ¿cuales serian las
> (practicamente) imprescindibles para un uso 'normal' de excel?.
Lo que se podría considerar "referencias mínimas", te lo indica el propio Excel al abrir un libro nuevo.
Abre el cuadro de 'Referencias', e intenta desmarcar las que lo estén, las que no te lo permita serán las imprescindibles.
Todo lo demás son 'extras' que puedes ir agregando a conveniencia, y no se requieren para nada en general, sino para algo en concreto. Siempre teniendo en cuenta que varias de las referencias y controles adicionales que a tí te aparezcan, pueden no estar presentes en el ordenador de otro usuario, y por consiguiente, puede que abras un libro en otra máquina y obtengas errores de carga dependiendo de las librerías referenciadas en tu libro.
Normalmente los problemas se suelen presentar por controles de terceros, que basta instalar/registrar en la máquina destino, aunque supongo que también dependerá del tipo de instalación del Office que realizó el usuario.
> Seria sobre todo para dejar las minimas y cargar solo las especificas para
> determinados casos, como el que propones.
Regresando al comentario del control de 'Adobe' que he mencionado, si después de haberlo añadido lo decidieses eliminar del formulario, la referencia puede permanecer activada. Ese es el problema en el que debes encontrarte ahora, y -como ya te puedes imaginar- sería difícil indicarte cuales te sobran, porque depende de los controles usados en el formulario, del tipo de variables que usa tu código, etc...
> > y aunque de hecho puedes indentar los textos, pregunto,
> > ¿has pensado en usar un control 'TreeView' para mostrarlos?.
>
> ...despues de echarle un ojo, creo que efectivamente me podria valer,
> ...pero tambien me interesria que luego se pudieran traspasar a una hoja
En el ejemplo que te he preparado, verás (una posible forma de) cómo puedes "exportar" el resultado a una hoja.
> 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 ....?"
En principio, creo que (si lo tienes) deberías agregar el de la 'MSComCtl.ocx' (que si no estoy equivocado, el componente se instala con el Office), aunque (otra vez no sé si estaré equivocado) creo que el fichero de ayuda se instala con Visual Basic 6, ...al menos siempre puedes consultar la MSDN Online
> > 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».
>
> ...se trata de un proyecto
> ...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
> ...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,
> ...
Bueno, este tipo de situación la he vivido varias veces, y creo que es lo normal (al menos, yo lo veo como una evolución natural). En mi caso, cada vez que voy acumulando nuevos conocimientos, y veo que puedo aplicar algunas mejoras en trabajos existentes (aunque en muchos casos sea sólo por practicar), voy realizando modificaciones. Pero hay ocasiones que en proyectos grandes, el cambio es excesivo, y una de dos, o se pierde demasiado tiempo "atando cabos" (por muy modulado que esté), y/o consigues obtener un proyecto potencialmente inestable.
Como ya me he dado "de morros" más de una vez, cada vez que preveo cambios notablemente grandes, dejo el fichero "para la posteridad", y creo una versión nueva del libro apoyándome en los requisitos, aprovechando sólo partes concretas que no necesitan cambio, o que requieran modificaciones ligeras, y vuelvo a comenzar.
Ya sé que parece una barbaridad.
Ojo, no es una recomendación para tu caso, simplemente te comento mi caso particular...
> 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.
Desde luego que siempre es mejor si se pueden aplicar los cambios en lo ya desarrollado.
Intenté un vez modificar un libro que ya tenía unas 15000 líneas de código, con nuevas técnicas que me hubieran permitido reducir el tamaño, una ejecución más rápida, más eficiente, etc..., y lo único que conseguí fue un berenjenal que pagué perdiendo mis vacaciones.
Pero como te decía, unas veces (posiblemente la mayoría) no son cambios muy traumáticos, y en cambio hay ocasiones que es inevitable... Por supuesto, si en tu caso puedes evitar comenzar de nuevo, será lo mejor.
> llevo tiempo intrigado con el funcionamiento de las funciones
> recursivas, aunque todavia no he intentado probar ninguna. Quizas vaya
> siendo hora....
En el ejemplo que te he preparado verás dos o tres. Y verás que no son nada complicado, son simplemente funciones que se llaman a sí mismas mientras no se alcanza una condición de salida. El caso más típico es el del cálculo del factorial de un número (aplicable a efectos docentes, y, poqué no, por elegancia estructural).
Verás en el ejemplo una forma recursiva de recorrer toda la descendencia de un nodo específico de un TreeView.
>
> 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' .
El uso práctico lo ves cuando necesitas un procedimiento que modifique el valor de un argumento que se le transfiere. Si el argumento lo pasas por valor pasarás una copia. Aclaro: Cuando declaras una variable se reserva un espacio en memoria, de tamaño acorde a su tipo. A través de esa variable obtendrás/modificarás el contenido de ese espacio de la memoria. Al pasar la variable por valor, se reserva otro espacio en memoria, y se le asigna el valor contenido en el espacio al que apuntaba la variable. Entonces, en la función llamada, la variable apuntará a ese nuevo espacio de memoria reservado, y no leerás/modificarás el contenido original.
Por consiguiente, al pasar el argumento como ByVal, cualquier cambio que realices sobre esa variable sólo tendrá efecto localmente en la función llamada, la variable que se pasó desde el procedimiento llamante siguirá conservando su valor original al regresar de la llamada.
Por el contrario, si el argumento se pasa por referencia, estarás pasando el apuntador a la posición en memoria del contenido, y lógicamente la variable del procedimiento llamante reflejará las modificaciones que se hayan hecho dentro de la función llamada.
El siguiente código es un caso típico con el que es posible ver claramente el asunto. Examina los resultados en la ventana 'Inmediato' (más abajo ya te comento lo que consultabas acerca de esta ventana)
'********************
Sub Test()
Dim intByRef As Integer 'la pasaremos por referencia
Dim intByVal As Integer 'la pasaremos por valor
intByRef = 2: intByVal = 2
Debug.Print "***** Test ByRef vs ByVal *****"
Debug.Print "Posición en memoria de intByRef: " & VarPtr(intByRef)
Debug.Print "Posición en memoria de intByVal: " & VarPtr(intByVal)
Call Duplicar(intByRef, intByVal)
Debug.Print "Por referencia se ha duplicado el valor :: " & intByRef
Debug.Print "Por valor ha conservado el valor pasado :: " & intByVal
Call Duplicar(intByRef, intByVal)
Debug.Print "ByRef ha cuatriplicado el valor original :: " & intByRef
Debug.Print "ByVal no produjo cambio alguno en la variable original :: " & intByVal
End Sub
Sub Duplicar(ByRef intRef As Integer, ByVal intVal As Integer)
'la dirección de memoria apuntada por "intRef" es la misma
' que la apuntada por "intByRef" en el procedimiento "Test"
Debug.Print "Posición en memoria de intRef: " & VarPtr(intRef)
intRef = intRef * 2
'se ha reservado un nuevo espacio en memoria para almacenar el valor pasado,
' la dirección de memoria apuntada por "intVal" es la de la nueva variable
' que se ha creado, y no la del procedimiento llamante
Debug.Print "Posición en memoria de intVal: " & VarPtr(intVal)
intVal = intVal * 2
End Sub
'********************
Igualmente, te comento que un caso práctico es el ejemplo de la descomposición de un entero largo, que representa un color, a sus valores RGB (cantidad de rojo, verde y azul). La función recibe el color y tres variables a las que "cederá" el resultado la función llamada:
'==============
Sub Test()
Dim R As Integer, G As Integer, B As Integer
'RGB es una función incorporada en VBA, que hace justamente lo contrario,
' recibe las tres cantidades de cada color, y devuelve su correspondiente entero largo
' Mostramos un valor de comparación
Debug.Print "RGB(50, 100, 200) = " & RGB(50, 100, 200)
'Obtenemos la descomposición de este valor
Call DescomponerRGB(RGB(50, 100, 200), R, G, B)
'Resultado de la descomposición
Debug.Print RGB(R, G, B) & " = RGB(" & R & ", " & G & ", " & B & ")"
End Sub
'Recuerda que si no se indica el tipo en la declaración, predeterminadamente se pasa por referencia.
Sub DescomponerRGB(ByVal lngColor As Long, Rojo As Integer, Verde As Integer, Azul As Integer)
Rojo = lngColor And &HFF&
Verde = (lngColor And &HFF00&) \ 256
Azul = (lngColor And &HFF0000&) \ 65536
End Sub
'==============
>
> 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.
Ventanas como la «Inmediato, Inspecciones, Locales», tienen esa característica. Si abres el menú contextual con el botón derecho sobre esas ventanas, verás una opción que dice "Acoplable". Cuando la opción está desmarcada se comporta como una ventana más del editor (lo que comentas que te sucede ahora), y cuando la opción está marcada es cuando puedes "anclarla" a un lateral o arriba o abajo.
Bueno, ya referente al ejemplo que te comenté, lo puedes descargar de la siguiente dirección:
http://www.telefonica.net/web2/vbutils/Excel_NewsGroup/UserForms.zip
Contiene dos ficheros, el del formulario y un '.frx' que contiene las imágenes usadas por el formulario.
Abres el fichero de Excel que contenga los formularios que quieres revisar, e importas sólo el fichero '.frm' al proyecto. En la sección "Declaraciones" del código del formulario están de recordatorio las opciones y referencias necesarias para que el formulario se pueda ejecutar correctamente.
He añadido comentarios, pero ya sabes que puedes preguntar si algo no lo ves claro. Es recomendable en algunas ocasiones ejecutarlo en modo 'Paso a paso por instrucciones {F8}', para seguir la ruta de ejecución y ver cómo se realizan las acciones.
El control de errores, lo dejo a deber ;-)
muchas gracias por tu tiempo y por tu extensa (y completa) ayuda.
Con tus respuestas ( y, para hacer justicia, con la de otros cuantos
colaboradores de los foros) estoy consiguiendo encarrilar hacia algo
parecido a la comprension una gran parte de mis actuales dudas, y no
sabes como te lo agradezco, porque ademas, una gran parte de ellas,
nacen (se obcecan) a causa de mi inseguridad. Y eso es dificil de
resolver solamente con la ayuda en linea.
tambien ayuda bastante saber que los atolladeros en los que uno se mete
aprendiendo por su cuenta algo como VBA, no son patrimonio propio, sino
algo relativamente normal ( quizas sea eso de " ¿mal? de muchos,
consuelo de ....")
en cuanto a tu respuesta no he podido mas que hacer una lectura rapida
( ya me he bajado el archivo), pero, como tu decias, parece que tiene
'tela', y de la buena.
Por eso he querido darte las gracias ahora, pues creo que analizarlo me
llevara su tiempo. Cuando vaya avanzando, es un honor saber que puedo
seguir contando con tu inestimable ayuda para las dudas.
Muchas gracias de nuevo, y, un saludo y hasta pronto
Ivan
PD:-> no se si entre tus actividades estará la docencia, pero si no es
asi, no sabe el mundo academico la 'joya' que se ha perdido.
PPD:-> de momento habia conseguido +o- la clasificacion de controles,
dando dos 'pasadas', una primera cargando los 'contenedores' y sus
prop. en la hoja, y una segunda con el resto de los controles usando
los rangos ya cargados y condicionales tipo:->
If Celda = TypeName(Ctl)
-> o/y -> If Celda.Ofsset(x,y) > Ctl.TopIndex
pero estoy dandole vueltas a las f. recursivas y demas. Voy a ponerme
duro con tu ayuda. Un saludo de nuevo.
y una segunda con el resto de los controles usando
> los rangos ya cargados y condicionales tipo:->
>
----------> If Celda = Ctl.Parent
<-------------------- o similar
un saludo
Ivan
lo primero decirte que tu formulario, en si mismo, es una maravilla (y,
en mi opinion, una practica herramienta para quien quiera trabajar, o
al menos aprender, sobre formularios), pero si le añadimos tus
comentarios en el codigo, se convierte en un verdadero manual para
avanzar mas rapido por estos 'sembraos' del VBA.
aunque ando con muy poquito tiempo, lo he imprimido y ya esta casi
ilegible, a base de subrallados, notas, etc.. De momento me estoy
centrando en utilizarlo para intentar aprender algo sobre colecciones y
matrices (con ambas estoy totalmente 'pez'), asi como sobre 'giros
sintacticos', uso de 'elementos' hasta ahora desconocidos para mi y
'estructuracion' de los codigos.
En cuanto al TreeView, de momento, y aunque me he bajado sus
propiedades y metodos de MSDN, aun no le he intentado meter mano. (por
cierto, por lo que creo entender, se trata de un contenedor de objetos.
O sea que se almacenan como tales, pudiendo acceder a sus propiedades,
metodos, etc, directamente. ¿o estoy equivocado?.
bueno, aunque me han surgido infinidad de (seguramente productivas)
dudas -¿Begin?, objPtr,....., y muchas mas-, creo que el resolverlas
es cuestion de ir poco a poco, y, como suele decir HM (+o-) , avanzar
al ritmo que uno mismo puede, quiere, le interesa, necesita,.......,
sabiendo que siempre hay gente 'ahi' para recurrir cuando uno se atora
en el camino
dicha mi horterada de turno, te comento que estoy intentando ordenar
los controles por orden del Tabindex o Index en su caso. Al llegar a
un objeto contenedor, irian sus respectivos 'contenidos' tambien
ordenados, retomando el orden correspondiente al Parent al acabarse
estos. En realidad se trata de un puro ejercicio, para practicar con
cosas nuevas. Pero ahora me he 'atorado' en un intento de funcion
recursiva, en la que seguramente estoy haciendo mas de una cosa mal.
para las practicas estoy usando un formulario con varios multipage
anidados, ademas de otros muchos controles. De momento he conseguido
'recoger' ordenados los contenidos en un multipage(y sus tres paginas)
tras avanzar a traves de otro y su pagina correspondiente., pero no
consigo salir de el e ir a la siguiente pagina del multipagina
contenedor.
expongo los codigos que intervienen, aunque son varios y quzas ocupen
mucho. De todas formas, como te digo, es solo por practicar, o sea que
no tengas (tengais) ninguna prisa ni compromiso por responder. El
principal motivo de este mensaje era confirmarte que habia probado tu
archivo, y que me esta viniendo muy bien. Muchas gracias de nuevo.
Un saludo y hasta protnto.
Ivan
'********************************************************************************
'la intencion es que devuelva todos los controles y/o objetos
contenidos
'en otro por orden de sus sucesivos TabIndex o Index
'funciona pero no acaba
'---------------------------------------------------------
Function Siguiente(ByVal oActual As Object, _
Optional ByVal PrimerPadre As Object) As Object
If IsMissing(PrimerPadre) Then Set PrimerPadre = oActual.Parent
Dim Ti As Long, TtObjs As Long, TtPags As Long
If nroCtlsYPagsEn(oActual) > 0 Then
Set Siguiente = SiguienteHijo(oActual): Exit Function
Else
If TypeName(oActual) = "Page" Then
Ti = oActual.Index: TtObjs = oActual.Parent.Pages.Count - 1
Else
Ti = oActual.TabIndex
TtObjs = nroObjsHijo(oActual.Parent) - 1
End If
If Ti < TtObjs Then
Set Siguiente = SiguienteHijo(oActual.Parent, Ti)
Exit Function
Else
If oActual.Parent Is PrimerPadre Then Exit Function
If TypeName(oActual.Parent) = "Page" Then
Ti = oActual.Parent.Index: TtObjs =
oActual.Parent.Parent.Pages.Count - 1
Else
Ti = oActual.Parent.TabIndex
TtObjs = nroObjsHijo(oActual.Parent.Parent) - 1
End If
If Ti < TtObjs Then
Set Siguiente = SiguienteHijo(oActual.Parent.Parent,
Ti)
Else
If oActual.Parent.Parent Is PrimerPadre Then Exit
Function
Set Siguiente = Siguiente(oActual.Parent.Parent)
End If
End If
End If
End Function
'*******************'SiguienteHijo'*****29/11/2006 20:26:22
************
'devuelve el siguiente objeto hijo de un objeto contenedor pasando como
'argumentos el objeto padre y el ultimo TabIndex(para un padre Control
'o Page), o el ultimo Index(para un padre MultiPage). Si el numero
pasado
'es igual o mayor que la cantidad de objetos contenidos devueve
Nothing.
'Si el objeto no contiene hijos devuelve Nothing. El 2º argumento es _
'opcional, si no se incluye se devuelve el 1er hijo del objeto padre.
'-----------------------------------------------------------------------
Function SiguienteHijo(ByVal oPadre As Object, _
Optional ByVal lTi) As Object
If IsMissing(lTi) Then lTi = -1
Dim oHijo As Object
If TypeName(oPadre) = "MultiPage" Then
If lTi < oPadre.Pages.Count - 1 Then
Set SiguienteHijo = oPadre(lTi + 1): Exit Function
End If
Else
On Error Resume Next
If oPadre.Controls.Count = 0 Then Exit Function
If lTi = oPadre.Controls.Count - 1 Then Exit Function
For Each oHijo In oPadre.Controls
If oHijo.Parent Is oPadre And oHijo.TabIndex = lTi + 1 Then
Set SiguienteHijo = oHijo: Exit Function
End If
Next
End If
End Function
'**********<26-11-06>*********************************************
'devuelve el Nº TOTAL de CONTROLES + Nº De PAGINAS en
'un objeto si alguno de los controles es un "MultiPage"
'------------------------------------------------------
Function nroCtlsYPagsEn(ByVal objCt As Object) As Long
Dim ctlOb As Control, ctlPg As Page, np As Long
nroCtlsYPagsEn = 0
If TypeName(objCt) = "MultiPage" Then
nroCtlsYPagsEn = nroCtlsYPagsEn + objCt.Pages.Count
For np = 0 To objCt.Pages.Count - 1
nroCtlsYPagsEn = nroCtlsYPagsEn + objCt(np).Controls.Count
For Each ctlOb In objCt(np).Controls
If TypeName(ctlOb) = "MultiPage" Then _
nroCtlsYPagsEn = nroCtlsYPagsEn + ctlOb.Pages.Count
Next
Next
Else
If nroCtlsEn(objCt) > 0 Then
nroCtlsYPagsEn = nroCtlsYPagsEn + objCt.Controls.Count
For Each ctlOb In objCt.Controls
If TypeName(ctlOb) = "MultiPage" Then _
nroCtlsYPagsEn = nroCtlsYPagsEn + ctlOb.Pages.Count
Next
End If
End If
End Function
''***********'nroObjsHijo'****<26-11-06>**************************
''devuelve el Nº de OBJETOS HIJO de un objeto (CON pages)
''----------------------------------------------------------------
Function nroObjsHijo(ByVal objCt As Object) As Long
Dim ctI As Control
nroObjsHijo = 0
If TypeName(objCt) <> "MultiPage" Then
If EsFormulario(objCt.Name) Or TypeName(objCt) = "Frame" _
Or TypeName(objCt) = "Page" Then
For Each ctI In objCt.Controls
If ctI.Parent Is objCt Then nroObjsHijo = nroObjsHijo + 1
Next
End If
Else
nroObjsHijo = objCt.Pages.Count
End If
End Function
lo primero comentar que la funcion que en el anterior mensaje
'pretendia llegar a recursiva' mas bien iba para obsesiva' (como su
hacedor) y retorcida. De momento he conseguido dejarla como se ve al
final.
bueno, creo que ya lo he conseguido, (aunque ha sido segun escribia
este mensaje (que en realidad era para consultar una (pen)ultima duda
que me estaba atascando, y que he visto segun escribia la consulta)).
Seguramente es una chapuza innecesaria, pero no ha sido mal ejercicio.
Ahora intentare aplicarlo al TreeView.
Por si quieres/quereis comentar algo expongo los codigos finales (+o-)
. Muchas gracias de nuevo y hasta pronto
Ivan
'*******intento de funcion recursiva****03/12/2006 23:45:11 *****
'para volver al 1er 'siguiente' control/pagina cuando se llega
'al ultimo objeto ( por orden de TabIndex o Index) de un 'contenedor'
'----------------------------------------------------------------
Function AnteriorSig(ByRef oSubir As Object, _
ByVal oPadreInicio As Object) As Object
Dim obX As Object
Dim Indice As Long, HijosEn As Long
Set obX = oSubir.Parent
If TypeName(obX) = "Page" Then
Indice = obX.Index: HijosEn = obX.Parent.Pages.Count - 1
Else
Indice = obX.TabIndex: HijosEn = nroObjsHijo(obX.Parent) - 1
End If
If obX Is oPadreInicio And Indice = HijosEn Then Exit Function
If Indice < HijosEn Then
Set AnteriorSig = SiguienteHijo(obX.Parent, Indice)
Else
Set AnteriorSig = AnteriorSig(obX, oPadreInicio)
End If
End Function
''***********'ObjetosHijoen'****<26-11-06>**************************
''devuelve el Nº de OBJETOS HIJO de un objeto
''----------------------------------------------------------------
Function ObjetosHijoEn(ByVal objCt As Object) As Long
Dim ctI As Control
ObjetosHijoEn = 0
If TypeName(objCt) <> "MultiPage" Then
If NroObjetosEn(objCt) > 0 Then
For Each ctI In objCt.Controls
If ctI.Parent Is objCt Then ObjetosHijoEn = ObjetosHijoEn + 1
Next
End If
Else
ObjetosHijoEn = objCt.Pages.Count
End If
End Function
'*************'NroObjetosEn'****<26-11-06>*******************
'devuelve el Nº total de CONTROLES en un objeto excepto
'si es un "MultiPage", en cuyo caso devuelve el Nº de PAGINAS
'------------------------------------------------------------
Function NroObjetosEn(ByVal objCt As Object) As Long
NroObjetosEn = 0
If TypeName(objCt) = "MultiPage" Then
NroObjetosEn = objCt.Pages.Count
Else
On Error Resume Next
NroObjetosEn = objCt.Controls.Count
On Error GoTo 0
End If
End Function
'**********<26-11-06>*********************************************
'devuelve el Nº TOTAL de CONTROLES + Nº De PAGINAS (si alguno _
'de los controles es un "MultiPage") en un objeto
'-----------------------------------------------------------------
Function NroCtsYPgsEn(ByVal objCt As Object) As Long
Dim ctlOb As Control, ctlPg As Page, np As Long
NroCtsYPgsEn = 0
If TypeName(objCt) = "MultiPage" Then
NroCtsYPgsEn = NroCtsYPgsEn + objCt.Pages.Count
For np = 0 To objCt.Pages.Count - 1
NroCtsYPgsEn = NroCtsYPgsEn + objCt(np).Controls.Count
For Each ctlOb In objCt(np).Controls
If TypeName(ctlOb) = "MultiPage" Then _
NroCtsYPgsEn = NroCtsYPgsEn + ctlOb.Pages.Count
Next
Next
Else
If nroCtlsEn(objCt) > 0 Then
NroCtsYPgsEn = NroCtsYPgsEn + objCt.Controls.Count
For Each ctlOb In objCt.Controls
If TypeName(ctlOb) = "MultiPage" Then _
NroCtsYPgsEn = NroCtsYPgsEn + ctlOb.Pages.Count
Next
End If
End If
End Function
'*******************'SiguHijo'*****29/11/2006 20:26:22 ************
'devuelve el siguiente objeto hijo de un objeto contenedor pasando como
'argumentos el objeto padre y el ultimo TabIndex(para un padre Control
'o Page), o el ultimo Index(para un padre MultiPage). Si el numero
pasado
'es igual o mayor que la cantidad de objetos contenidos devueve
Nothing.
'Si el objeto no contiene hijos devuelve Nothing. El 2º argumento es _
'opcional, si no se incluye se devuelve el 1er hijo del objeto padre.
'-----------------------------------------------------------------------
Function SiguHijo(ByVal oPadre As Object, _
Optional ByVal lTi) As Object
If IsMissing(lTi) Then lTi = -1
Dim oHijo As Object
If TypeName(oPadre) = "MultiPage" Then
If lTi < oPadre.Pages.Count - 1 Then
Set SiguHijo = oPadre(lTi + 1): Exit Function
End If
Else
On Error Resume Next
If oPadre.Controls.Count = 0 Then Exit Function
For Each oHijo In oPadre.Controls
If oHijo.Parent Is oPadre And oHijo.TabIndex = lTi + 1 Then
Set SiguHijo = oHijo: Exit Function
End If
Next
End If
End Function
'------------------------------------------------------------------------------------------
'Nota: los controles los he cargado en una hoja con otro codigo.
'Y me falta salvar el error cuando llega al ultimo objeto/control
'*******************Codigos para las pruebas*************************
'-------------------------------------------------------------------------------------------
Sub Test_Recursiva_2(ByVal oObjForm As Object)
Dim ct As Object
Dim fil As Long, nTI As Long, fin As Long, Ttl As Long
Set ct = SiguHijo(oObjForm)
If TypeName(ct) = "Page" Then nTI = ct.Index Else _
nTI = ct.TabIndex
Ttl = ObjetosHijoEn(ct.Parent)
With Hoja2
.[a:g].Clear
.[a1:e1] = Array("Objeto/Control", "Posicion", _
"ObjetoPadre", "Tab/Index", "Tipo")
.[a2] = ct.Parent.Name: .[b2] = 0
.[a3:e3] = Array(ct.Name, 1, ct.Parent.Name, nTI, _
TypeName(ct))
fin = NroCtsYPgsEn(oObjForm) + 3
For fil = 4 To fin
If TypeName(ct) = "Page" Then nTI = ct.Index Else _
nTI = ct.TabIndex
Ttl = ObjetosHijoEn(ct.Parent)
If NroObjetosEn(ct) > 0 Then
Set ct = SiguHijo(ct)
If ct Is Nothing Then Set ct = AnteriorSig(ct, oObjForm)
Else
If nTI < Ttl - 1 Then Set ct = SiguHijo(ct.Parent, nTI) _
Else Set ct = AnteriorSig(ct, oObjForm)
End If
On Error Resume Next
If TypeName(ct) = "Page" Then nTI = ct.Index Else _
nTI = ct.TabIndex
.Range("a" & fil & ":e" & fil) = Array(ct.Name, fil - 2, _
ct.Parent.Name, nTI, TypeName(ct))
On Error GoTo 0
Next
.Columns.AutoFit
End With
Set ct = Nothing
End Sub
'********************************************************
Sub Test_Recursiva_1()
Call Test_Recursiva_2(frmMulti)
End Sub
'---------------------------------------------The
End--------------------------------