none
¿Cómo puedo obtener una referencia al formulario que llama a un procedimiento?

    Pregunta

  • Hola a todos, Felices Fiestas!!!

    Entiendo que la manera más fácil de hacer lo que consulto es pasándole Me como prámetro al procedimiento, otra forma sería declarando una variable global que se vaya actualizando en cada formulario y consultándola en el procedimiento.

    Pero me gustaria algo más automático, vía programación.

    Me parece que se puede usando System.Reflection, de manera que devuelva una referencia o el nombre del formulario desde el cual se ejecuta el procedimiento si es que se ejecuta desde alguno ¿Cómo sería?



    • Editado James-2016 lunes, 26 de diciembre de 2016 18:56
    lunes, 26 de diciembre de 2016 18:55

Respuestas

  • "James-2016" preguntó:

    > ¿Cómo puedo obtener una referencia al formulario que llama a un procedimiento?
    > Entiendo que la manera más fácil de hacer lo que consulto es pasándole Me como
    > prámetro al procedimiento, ...

    Pues entonces, ¿para qué complicarse más la vida? ;-)

    Tenemos un método que contiene un parámetro con el tipo de dato System.Windows.Forms.Form:

       Friend Sub NombreMetodo(frm As Form)
    
            If (frm Is Nothing) Then
                Throw New ArgumentNullException()
            End If
    
            MessageBox.Show(frm.Name)
    
        End Sub

    Y desde el formulario llamador ejecutamos el método:

        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    
            ' Llamamos al método pasándole la referencia
            ' del formulario donde actualmente se está
            ' ejecutando éste código.
            NombreMetodo(Me)
    
        End Sub

    > Me parece que se puede usando System.Reflection, de manera que devuelva
    > una referencia o el nombre del formulario desde el cual se ejecuta el
    > procedimiento si es que se ejecuta desde alguno

    ¿De verdad crees que merece la pena utilizar la reflexión para lo que dices que deseas obtener, la referencia del formulario llamador o cliente?

    Si deseas obtener la referencia del formulario llamador, digo yo que será porque desde ese método vas a trabajar con él, por tanto, si conoces el formulario, pásale su referencia sin más.

    ¿Quieres un consejo? Deja la reflexión para otras menesteres más complicados. ;-)

    Un saludo y ¡Feliz Navidad!


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.






    lunes, 26 de diciembre de 2016 19:10
    Moderador
  • "James-2016" preguntó:

    > Entonces lo planteo de otra manera, no habría forma de obtener el nombre
    > del formulario desde que se ejecuta un procedimiento sin que se le tenga
    > que pasar un parámetro?

    Haberla, la hay, mediante reflexión, pero no es tan directa y "automática" como hacer que ese método tenga un parámetro para pasarle la REFERENCIA ACTUAL (toma buena nota de las palabras mayúsculas) del formulario.

    Aquí tienes el código que tienes que ejecutar. Vamos a suponer que el método en cuestión (que no tiene ningún parámetro de entrada) lo tienes insertado en un Module del proyecto actual:

    Imports System.Reflection
    
    Friend Module Module1
    
        Friend Sub NombreMetodo()
    
            ' Obtenemos el objeto MethodBase del último método llamador.
            Dim method As MethodBase = GetCallerMethod(1)
    
            If (Not method Is Nothing) Then
    
                ' Obtenemos el System.Type de la clase a la que corresponde el
                ' método obtenido mediante el nombre completo de su clase.
                Dim ty As Type = Type.GetType(method.ReflectedType.FullName, True)
    
                ' Indicadores para crear una instancia de la clase.
                Dim flags As BindingFlags = BindingFlags.Default Or BindingFlags.CreateInstance
    
                ' Creamos una nueva instancia de la clase invocando a su constructor público sin parámetros.
                ' Al crear una nueva instancia, ésta tendrá los valores por defecto que tengan los campos
                ' definidos en ella, incluidos los valores por defecto de los diferentes controles existentes
                ' en el formulario.
                '
                Dim frm As Form = DirectCast(ty.InvokeMember("", flags, Nothing, Nothing, Nothing), Form)
    
                ' Mostramos el nombre del formulario.
                MessageBox.Show(frm.Name)
    
            End If
    
        End Sub
    
        ''' <summary>
        ''' Devuelve el objeto MethodBase correspondiente a un método
        ''' del seguimiento de la pila que ha efectuado la llamada. 
        ''' </summary>
        ''' <param name="methodsToSkip">Número de métodos que se pasarán
        ''' por alto desde el seguimiento de la pila.</param>
        ''' <returns></returns>
        Private Function GetCallerMethod(methodsToSkip As Integer) As MethodBase
    
            If (methodsToSkip < 0) Then
                Throw New ArgumentOutOfRangeException
            End If
    
            Dim method As MethodBase = Nothing
    
            ' Al número de métodos especificados que se pasarán por alto hay que sumarle
            ' una unidad, para que no se incluya el propio método GetCallerMethod. Es decir,
            ' si se pasa el valor 0, la función devolverá el propio método GetCallerMethod
            ' salvo que le añada una unidad.
            '
            Dim frame As StackFrame = New StackTrace((methodsToSkip + 1)).GetFrame(0)
    
            If (Not frame Is Nothing) Then
                method = frame.GetMethod()
            End If
    
            Return method
    
        End Function
    
    End Module

    Al final he podido resumir el código fuente en unas 17 líneas, incluida la función auxiliar GetCallerMethod. Las líneas de los comentarios no se cuentan, ya que no se compilan.

    Fíjate que todo empieza por averiguar el objeto MethodBase del método del formulario que ha llamado al método NombreMetodo existente en Module1. Y para obtener el MethodBase, hay que recorrer la pila de llamadas, que es de lo que se encarga la función GetCallerMethod, que dependiendo del número que se le pase así nos devolverá el objeto MethodBase apropiado.

    Y cuando desees llamar al método, desde el evento Click de un control Button existente en un formulario, invocarías al método:

        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Module1.NombreMetodo()
        End Sub

    Pero lo dicho, que lo único que vas a obtener es el nombre del formulario (Form1, Form2, frmClientes, o como se llame el formulario), NO la REFERENCIA que ACTUALMENTE se ESTÁ EJECUTANDO de dicho formulario. Como te he indicado en la respuesta anterior, únicamente crearás una NUEVA REFERENCIA del formulario, como si desde NombreMetodo ejecutaras:

        Dim frm As New FormXXX()

    Lo mismo hay otra manera más directa y sencilla, sin escribir 17 líneas de código, pero por ahora, yo la desconozco. ;-)

    Si por una casualidad de la vida deseas conocer el nombre del formulario que ha invocado al método para precisamente mostrar en pantalla otra instancia del mismo, después del MessageBox.Show(), efectúa una llamada al método Show o ShowDialog del formulario referenciado:

        frm.ShowDialog()

    Y observarás que el formulario se muestra con los valores por defecto que tengan sus campos y controles, que no tienen por qué ser los que actualmente tengan al invocar al método. ¿Me explico? ;-)

    Para esto último, lo mismo te hubiera servido lo que explico en el siguiente artículo, que prácticamente es lo mismo que te he explicado aquí, aunque obviamente no se obtiene el objeto MethodBase del último método que ha invocado al procedimiento NombreMetodo:

    Mostrar un formulario conocimiento su nombre


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.





    lunes, 26 de diciembre de 2016 21:59
    Moderador
  • "James-2016" escribió:

    > ... muy interesante y algo complejo el tema...

    La reflexión no es precisamente un tema fácil de entender si no se tienen los conocimientos adecuados. Pero para conocer la "historia" del método que ha llamado a otro método, tampoco es un tema que hay que estar luchando con él todos los días, por lo que hay que recurrir a la reflexión. Ya te aconsejé más arriba que dejaras la reflexión para otras tareas más complicadas. ;-)

    Cuando obtienes un error y te aparece en Visual Studio una "lista interminable" de métodos, ¿cómo crees que se obtiene esa lista? Pues analizando la pila de llamadas, que es lo que hace la función GetCallerMethod mostrada, pero ésta solamente te va a devolver el objeto MethodBase correspondiente al número de orden especificado: desde el último método (1) hasta el primero (n), devolviendo un valor Nothing si no existe un objeto MethodBase que se corresponda con el número de orden especificado.


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.

    • Marcado como respuesta James-2016 martes, 27 de diciembre de 2016 7:23
    martes, 27 de diciembre de 2016 7:08
    Moderador

Todas las respuestas

  • "James-2016" preguntó:

    > ¿Cómo puedo obtener una referencia al formulario que llama a un procedimiento?
    > Entiendo que la manera más fácil de hacer lo que consulto es pasándole Me como
    > prámetro al procedimiento, ...

    Pues entonces, ¿para qué complicarse más la vida? ;-)

    Tenemos un método que contiene un parámetro con el tipo de dato System.Windows.Forms.Form:

       Friend Sub NombreMetodo(frm As Form)
    
            If (frm Is Nothing) Then
                Throw New ArgumentNullException()
            End If
    
            MessageBox.Show(frm.Name)
    
        End Sub

    Y desde el formulario llamador ejecutamos el método:

        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    
            ' Llamamos al método pasándole la referencia
            ' del formulario donde actualmente se está
            ' ejecutando éste código.
            NombreMetodo(Me)
    
        End Sub

    > Me parece que se puede usando System.Reflection, de manera que devuelva
    > una referencia o el nombre del formulario desde el cual se ejecuta el
    > procedimiento si es que se ejecuta desde alguno

    ¿De verdad crees que merece la pena utilizar la reflexión para lo que dices que deseas obtener, la referencia del formulario llamador o cliente?

    Si deseas obtener la referencia del formulario llamador, digo yo que será porque desde ese método vas a trabajar con él, por tanto, si conoces el formulario, pásale su referencia sin más.

    ¿Quieres un consejo? Deja la reflexión para otras menesteres más complicados. ;-)

    Un saludo y ¡Feliz Navidad!


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.






    lunes, 26 de diciembre de 2016 19:10
    Moderador
  • Sí, bueno, como lo pone es la manera adecuada y como dice lo otro usarlo quizá para cosas más complejas, solo tenía curiosidad de como sería de manera automática.
    lunes, 26 de diciembre de 2016 19:55
  • "James-2016" escribió:

    solo tenía curiosidad de como sería de manera automática.

    La manera "automática" sería pasándole la referencia propiamente dicha del formulario, mediante la palabra clave Me o mediante una variable del tipo Form que almacene una referencia válida del formulario en cuestión.

    Pero mediante reflexión, aparte de escribir entre 20-30 líneas de código, NUNCA tendrías una referencia del formulario que actualmente se está ejecutando, y por ende, el que ha invocado al método. Como mucho podrías crear una NUEVA INSTANCIA DEL FORMULARIO, y acceder a los valores que tengan por defecto los campos definidos en el mismo, incluidos los de los controles del formulario, pero en ningún caso la referencia de la instancia del formulario que actualmente se está ejecutando. Para ello, o le pasas la palabra clave Me o una variable objeto que tengas declarada y que referencie al formulario.

    Si mediante reflexión se pudiera obtener una referencia válida de cualquier objeto en ejecución, ... ¡Apaga y vámonos! 😄

    Si tienes una referencia válida de cualquier objeto, entonces sí podrías acceder mediante reflexión a sus métodos y propiedades, tanto públicas como privadas, estas últimas en una ejecución del código de plena confianza. Pero si no tienes una referencia del objeto, lo único que puedes hacer es crear una nueva instancia del objeto, como si utilizaras la palabra clave New, y siempre y cuando el autor de la clase no haya escrito código propio para impedir  que se puedan crear nuevas instancias de la clase mediante reflexión.


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.



    lunes, 26 de diciembre de 2016 20:51
    Moderador
  • mm bueno, entiendo lo que explica y no le discuto, pero digo por ejemplo, si tengo varios formularios abiertos, conociendo el nombre de uno de ellos se puede obtener una referencia directa a ese formulario con una línea como esta:

    ReferenciaAlFormulario = Application.OpenForms().Item("NombreFormulario")

    Entonces lo planteo de otra manera, no habría forma de obtener el nombre del formulario desde que se ejecuta un procedimiento sin que se le tenga que pasar un parámetro?

    lunes, 26 de diciembre de 2016 21:22
  • "James-2016" preguntó:

    > Entonces lo planteo de otra manera, no habría forma de obtener el nombre
    > del formulario desde que se ejecuta un procedimiento sin que se le tenga
    > que pasar un parámetro?

    Haberla, la hay, mediante reflexión, pero no es tan directa y "automática" como hacer que ese método tenga un parámetro para pasarle la REFERENCIA ACTUAL (toma buena nota de las palabras mayúsculas) del formulario.

    Aquí tienes el código que tienes que ejecutar. Vamos a suponer que el método en cuestión (que no tiene ningún parámetro de entrada) lo tienes insertado en un Module del proyecto actual:

    Imports System.Reflection
    
    Friend Module Module1
    
        Friend Sub NombreMetodo()
    
            ' Obtenemos el objeto MethodBase del último método llamador.
            Dim method As MethodBase = GetCallerMethod(1)
    
            If (Not method Is Nothing) Then
    
                ' Obtenemos el System.Type de la clase a la que corresponde el
                ' método obtenido mediante el nombre completo de su clase.
                Dim ty As Type = Type.GetType(method.ReflectedType.FullName, True)
    
                ' Indicadores para crear una instancia de la clase.
                Dim flags As BindingFlags = BindingFlags.Default Or BindingFlags.CreateInstance
    
                ' Creamos una nueva instancia de la clase invocando a su constructor público sin parámetros.
                ' Al crear una nueva instancia, ésta tendrá los valores por defecto que tengan los campos
                ' definidos en ella, incluidos los valores por defecto de los diferentes controles existentes
                ' en el formulario.
                '
                Dim frm As Form = DirectCast(ty.InvokeMember("", flags, Nothing, Nothing, Nothing), Form)
    
                ' Mostramos el nombre del formulario.
                MessageBox.Show(frm.Name)
    
            End If
    
        End Sub
    
        ''' <summary>
        ''' Devuelve el objeto MethodBase correspondiente a un método
        ''' del seguimiento de la pila que ha efectuado la llamada. 
        ''' </summary>
        ''' <param name="methodsToSkip">Número de métodos que se pasarán
        ''' por alto desde el seguimiento de la pila.</param>
        ''' <returns></returns>
        Private Function GetCallerMethod(methodsToSkip As Integer) As MethodBase
    
            If (methodsToSkip < 0) Then
                Throw New ArgumentOutOfRangeException
            End If
    
            Dim method As MethodBase = Nothing
    
            ' Al número de métodos especificados que se pasarán por alto hay que sumarle
            ' una unidad, para que no se incluya el propio método GetCallerMethod. Es decir,
            ' si se pasa el valor 0, la función devolverá el propio método GetCallerMethod
            ' salvo que le añada una unidad.
            '
            Dim frame As StackFrame = New StackTrace((methodsToSkip + 1)).GetFrame(0)
    
            If (Not frame Is Nothing) Then
                method = frame.GetMethod()
            End If
    
            Return method
    
        End Function
    
    End Module

    Al final he podido resumir el código fuente en unas 17 líneas, incluida la función auxiliar GetCallerMethod. Las líneas de los comentarios no se cuentan, ya que no se compilan.

    Fíjate que todo empieza por averiguar el objeto MethodBase del método del formulario que ha llamado al método NombreMetodo existente en Module1. Y para obtener el MethodBase, hay que recorrer la pila de llamadas, que es de lo que se encarga la función GetCallerMethod, que dependiendo del número que se le pase así nos devolverá el objeto MethodBase apropiado.

    Y cuando desees llamar al método, desde el evento Click de un control Button existente en un formulario, invocarías al método:

        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Module1.NombreMetodo()
        End Sub

    Pero lo dicho, que lo único que vas a obtener es el nombre del formulario (Form1, Form2, frmClientes, o como se llame el formulario), NO la REFERENCIA que ACTUALMENTE se ESTÁ EJECUTANDO de dicho formulario. Como te he indicado en la respuesta anterior, únicamente crearás una NUEVA REFERENCIA del formulario, como si desde NombreMetodo ejecutaras:

        Dim frm As New FormXXX()

    Lo mismo hay otra manera más directa y sencilla, sin escribir 17 líneas de código, pero por ahora, yo la desconozco. ;-)

    Si por una casualidad de la vida deseas conocer el nombre del formulario que ha invocado al método para precisamente mostrar en pantalla otra instancia del mismo, después del MessageBox.Show(), efectúa una llamada al método Show o ShowDialog del formulario referenciado:

        frm.ShowDialog()

    Y observarás que el formulario se muestra con los valores por defecto que tengan sus campos y controles, que no tienen por qué ser los que actualmente tengan al invocar al método. ¿Me explico? ;-)

    Para esto último, lo mismo te hubiera servido lo que explico en el siguiente artículo, que prácticamente es lo mismo que te he explicado aquí, aunque obviamente no se obtiene el objeto MethodBase del último método que ha invocado al procedimiento NombreMetodo:

    Mostrar un formulario conocimiento su nombre


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.





    lunes, 26 de diciembre de 2016 21:59
    Moderador
  • mm muchas gracias, muy interesante y algo complejo el tema, interesante también su artículo.
    lunes, 26 de diciembre de 2016 23:13
  • "James-2016" escribió:

    > ... muy interesante y algo complejo el tema...

    La reflexión no es precisamente un tema fácil de entender si no se tienen los conocimientos adecuados. Pero para conocer la "historia" del método que ha llamado a otro método, tampoco es un tema que hay que estar luchando con él todos los días, por lo que hay que recurrir a la reflexión. Ya te aconsejé más arriba que dejaras la reflexión para otras tareas más complicadas. ;-)

    Cuando obtienes un error y te aparece en Visual Studio una "lista interminable" de métodos, ¿cómo crees que se obtiene esa lista? Pues analizando la pila de llamadas, que es lo que hace la función GetCallerMethod mostrada, pero ésta solamente te va a devolver el objeto MethodBase correspondiente al número de orden especificado: desde el último método (1) hasta el primero (n), devolviendo un valor Nothing si no existe un objeto MethodBase que se corresponda con el número de orden especificado.


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.

    • Marcado como respuesta James-2016 martes, 27 de diciembre de 2016 7:23
    martes, 27 de diciembre de 2016 7:08
    Moderador