Anda di halaman 1dari 22

Secuencia normal de eventos en VFP cuando se Crean y Destruyen Formularios

Tomado de What Your Mother Never Told You About Form Instantiation and Destruction presentada por el autor en la Conferencia DevEssentials Kansas, 2004)
Ledo y Editado a mi modo: jun/2013 Al final de este documento est disponible la traduccin original

Secuencia de eventos LISAG y QRDU


LISAG (Load-Init-Show-Activate-GotFocus) y QRDU (QueryUnload- Release- Destroy- Unload) NOTA: He tomado Form Instantiation como creacin del dan valores especficos del Form. Ya que es en ese momento se dan a las propiedades del Form y por lo tanto se crea un Form particular. particular. Resumen Esta sesin intenta ayudar a entender mejor la secuencia normal de eventos en VFP cuando se crean y destruyen formularios.

Para implementar exitosamente los escenarios de creacin y destruccin de un formulario, lo primero que debe entender es la secuencia nativa de los eventos. Creacin de un Formulario Aqu est la lista de eventos que ocurren durante la creacin: 1. Evento Form.DataEnvironment.OpenTables (Tablas/vistas en el Entorno de datos DataEnvironment no estn en uso USE() o abiertas) 2. Evento Form.DataEnvironment.BeforeOpenTables (Tablas/vistas en el Entorno de datos DataEnvironment no estn en uso USE() o abiertas) 3. Evento Form.Load (Tablas/vistas en el Entorno de datos DataEnvironment estn en uso USE() o abiertas) 4. Evento Init de cualquier objeto cursor en DataEnvironment 5. Evento Form.DataEnvironment.Init 6. Evento Init de cada miembro del formulario que es creado 7. Evento Form.Init 8. Evento Form.Show 9. Evento Form.Activate 10. Evento When del primer control del formulario en el orden de tabulacin (tab order) 11. Evento Form.GotFocus 12. Evento GotFocus del primer control del formulario en el orden de tabulacin Destruccin de un Formulario Formulario cerrado por el usuario: Esta es la lista de eventos que ocurren durante la destruccin, cuando el formulario es cerrado por el usuario haciendo Clic en la "X" en la esquina superior derecha de la barra de ttulo del formulario ... o ... por el usuario, seleccionando la opcin Cerrar desde el ControlBox en la esquina superior izquierda de la barra de ttulo del formulario: 1. Evento Form.QueryUnload

2. Evento Form.Destroy 3. Evento Destroy para cada uno de los miembros del formulario. 4. Evento Form.Unload (Tablas/vistas en el Entorno de datos DataEnvironment estn en uso USE() o abiertas) 5. Evento Form.DataEnvironment.CloseTables (Tablas/vistas en el Entorno de datos DataEnvironment no estn en uso USE() o abiertas) 6. Evento Form.DataEnvironment.Destroy 7. Evento Destroy para cada cursor en el DataEnvironment Formulario cerrado por una llamada: Aqu est la lista de eventos que ocurren cuando el formulario es cerrado por una llamada a THISFORM.Release(), por ejemplo, al hacer Clic en un botn Aceptar 1. Evento Form.Release 2. Evento Form.Destroy 3. Evento Destroy para cada uno de los miembros del formulario. 4. Evento Form.Unload (Tablas/vistas en el Entorno de datos DataEnvironment estn en uso USE() o abiertas) 5. Evento Form.DataEnvironment.CloseTables (Tablas/vistas en el Entorno de datos DataEnvironment no estn en uso USE() o abiertas) 6. Evento Form.DataEnvironment.Destroy 7. Evento Destroy para cada cursor en el DataEnvironment Creacin / Destruccin de miembros contenedores En la Creacin:

Los contenedores se crean "de dentro hacia afuera" de igual forma que hace el propio formulario (debido a que este, tambin es un contenedor). El Init de los miembros contenidos de disparan antes que el Init del contenedor padre. En la Destruccin:

Ocurre lo contrario, los contenedores se destruyen "de afuera hacia dentro". El Destroy del contenedor se dispara antes que el Destroy de sus miembros contenidos. Formulario con valores establecido para DEClass, DEClassLibrary Cuando un formulario establece valores para DEClass, DEClassLibrary (disponibles a partir de VFP 8.0), las cosas son ligeramente diferentes. Debido a que el objeto DataEnvironment es un objeto completamente separado, se crea completamente antes del Form.Load. El Init de los miembros del DataEnvironment (Es decir, el Init del Cursor, CursorAdapter, y Relation): Se disparan antes que el Init del DataEnvironment, siguiendo el comportamiento nativo de VFP donde el Init de los miembros ocurre antes que el Init del contenedor padre. El evento Destroy del DataEnvironment: Ocurre antes que el Destroy de sus miembros (Es decir, del Cursor, CursorAdapter, y Relation).

Aspectos de inters del Form: 1. Dependiendo de cmo se cierra el formulario, se ejecuta el mtodo Release o el evento QueryUnload; pero no ambos: El evento Form.Release no es un buen lugar para colocar cdigo, no importa lo que sea, que se deba ejecuta cada vez que se destruya el formulario. Utilice en su lugar los eventos Destroy o Unload. 2. Debido a que muchos comandos como SET TALK estn limitados a la sesin privada de datos (ver DATASESSION TO en la ayuda de VFP para ver la lista de estos comandos SET), decidir en que lugar va a asignar valores a estos comandos SET depende de cmo abre sus datos. Si no utiliza nunca DataEnvironment: Los comandos SET para la sesin privada de datos se colocan en el Load de la clase form. Si utiliza DataEnvironment nativo en un formulario basado en .SCX: Tendr que establecer estos comandos SET en DataEnvironment.OpenTables/BeforeOpenTables, antes de que las tablas/cursores sean abiertos. Si implementa un DataEnvironment de usuario especificado en las propiedades DEClass/DEClassLibrary (VFP 8.0 y superior): Podr establecer los comandos SET para la sesin privada de datos en el mtodo Init de la clase DataEnvironment; pero sea consciente, que el Init de los miembros Cursor, CursorAdapter, y los objetos Relation se disparan antes que DataEnvironment.Init. 3. Al destruir un formulario no se disparan los eventos Form.Deactivate ni Form.LostFocus DataEnvironment 1. El DataEnvironment abre sus datos implcitamente entre el DataEnvironment.BeforeOpenTables y el Form.Load(), inmediatamente antes del Form.Load. 2. El DataEnvironment NO abre los datos en el evento OpenTable -- OpenTables es de uso opcional para abrir programticamente los datos si se utiliza AutoOpenTables = .F. 3. El evento BeforeOpenTables no se dispara antes (Before) que el evento OpenTables, BeforeOpenTables est mal nombrado y se debi llamar algo como AfterOpenTablesBeforeImplicitOpenTables. (DespuesAbrirTablasAntesImplicitamenteAbrirTablas) 4. El DataEnvironment.Cursor.Init se dispara DESPUS que el cursor ya fue abierto y DESPUS del Form.Load 5. El DataEnvironment.Init se dispara DESPUS que el DataEnvironment halla cumplido todas las misiones encomendadas. DESPUS que los cursores han sido puestos en uso, y DESPUS del Form.Load. Excepto cuando el DataEnvironment es una creacin de usuario especificada en las propiedades DEClass/DEClassLibrary, en ese caso se crea completamente antes de Form.Load. 6. Consecuente con el comportamiento de OpenTables y cuando las tablas se abren explcitamente por el DataEnvironment, el mismo cierra las tablas ANTES del mtodo CloseTables. 7. Mientras la secuencia de creacin / destruccin para el formulario y sus miembros es consistente, la secuencia de creacin / destruccin para el DataEnvironment depende de su implementacin: Tipos de Implementacin DataEnvironment: 1.- DataEnvironment nativo para un formulario basado en .SCX, 2.- Implementacin de la propiedad DEClass DataEnvironment de usuario para formulario basado en .SCX o .VCX.

Llamar al mtodo THISFORM.Metodos o consultar THISFORM.Propiedades desde el DataEnvironment Los ejemplos muestran un comportamiento inconsistente relacionado con llamar a mtodos del formulario (THISFORM) desde el DataEnvironment. DataEnvironment nativo en un formulario basado en .SCX: Cuando un formulario basado en .SCX utiliza DataEnvironment nativo de VFP, el DataEnvironment se crea primero que el formulario como tal. Sin embargo

1. Las llamadas a mtodos de formulario (nativos o de usuario), desde mtodos del DataEnvironment que se disparan antes del Form.Load son COMPLETAMENTE IGNORADAS!. Por ejemplo, las llamadas a THISFORM.Metodos desde los eventos de DataEnvironment OpenTables y BeforeOpenTables no hacen nada, no se invoca ningn mtodo y no se genera un error. Supongo que VFP no ha iniciado todava la creacin del formulario y por tanto, no reconoce THISFORM como un objeto; pero yo debera esperar que se generara un error, como hace SYS(1271,THISFORM). 2. Las llamadas a mtodos de formulario (nativos o de usuario), desde mtodos del DataEnvironment que se disparan antes del Form.Load S disparan esos mtodos EN CASO que hayan sido definidos en la clase Form a partir de la cual hereda el formulario actual. Por supuesto, todos los mtodos nativos de VFP heredan de la clase base Form de VFP. Entonces, el cdigo que se ejecuta es el heredado de la clase Form, NO cualquier cdigo ubicado en los mtodos nativos o de usuarios del formulario creado. Una vez que se dispara el Form.Load, la llamada a estos mismos mtodos disparan los mtodos para el nivel creado. 3. De igual forma, si en el DataEnvironment que se dispara antes del Form.Load, consulta el valor de una propiedad (nativa o de usuario) que se establece explcitamente en la ficha Propiedades a nivel del formulario creado, los valores son los que sean predeterminados por VFP para esa propiedad (.F. para todas las propiedades de usuario), como si hubiera establecido las propiedades como predeterminadas en la ficha propiedades. Sin embargo; para las propiedades establecidas en la ventana propiedades de cualquier clase padre del formulario creado, el valor es evaluado adecuadamente, tal y como esperamos. Sin embargo, puede establecer una propiedad en cualquier mtodo del DataEnvironment. 4. En los mtodos de DataEnvironment, IntelliSense no muestra NINGUN PEM (Propiedades, Eventos, Mtodos) de usuario, ya sea de la creacin actual o de alguna clase padre de la creacin actual del formulario. Conclusin El DataEnvironment para mtodos de usuario de formulario, ya que estos mtodos estn basados en formularios .SCX. Y debe recordar que el cdigo que coloque en estos mtodos en el nivel creacin del .SCX ser completamente ignorado, cuando esos mtodos son llamados desde eventos del DataEnvironment como OpenTables y BeforeOpenTables que se ejecutan antes que Form.Load. Para este nivel de creacin establecer propiedades es poco confiable hasta que no se ejecute Form.Load. DataEnvironment de usuario especificado en las propiedades DEClass/DEClassLibrary para un formulario basado en .SCX Cuando las propiedades DEClass y DEClassLibrary de un formulario basado en .SCX tienen asignado un valor que especifique un objeto DataEnvironment de usuario, las cosas son algo diferentes: 1. En tiempo de diseo, cuando se da valor a las propiedades DEClass y DEClassLibrary asignando una clase DataEnvironment con cdigo en su evento Init, o en el Init de alguno de sus miembros cursor/relation, y ese Init llama a un mtodo de usuario de ese formulario, slo el hecho de asignar valor a las propiedades DEClass y DEClassLibrary genera un error "Objeto no contenido en el formulario" ("Object is not contained in a Form"), para cada llamada al mtodo de usuario del formulario (THISFORM). 2. En tiempo de ejecucin, se genera el mismo error "Objeto no contenido en el formulario. 3. En tiempo de ejecucin, aquellas mismas llamadas a mtodos de usuario de THISFORM que generaban un error cuando eran llamados desde el DataEnvironment.Init o desde el Init de uno de sus miembros, NO HACEN NADA si son llamados desde los eventos DataEnvironment.OpenTables o BeforeOpenTables ... los mtodos de usuario agregados al .SCX en la creacin actual son completamente ignorados. 4. En tiempo de ejecucin, cuando ocurren eventos como OpenTables and BeforeOpenTables del DataEnvironment, que se disparan despus del DataEnvironment.Init y el Init de sus miembros llaman a mtodos de THISFORM, slo se ejecuta el cdigo para aquellos mtodos que es heredado de la clase padre ... no el cdigo colocado en los mtodos del nivel actual creado. Conclusin La conclusin es que el cdigo colocado en mtodos de la instancia actual del formulario nunca se ejecuta cuando el mtodo es llamado desde eventos del DataEnvironment. Para confiar en los comportamientos abstractos del DataEnvironment, debe crear todo el cdigo en la clase DataEnvironment (jerrquicamente). Por ejemplo, algo que necesitamos a menudo es colocar SET TALK OFF antes de que se disparen los eventos del DataEnvironment, esto se debe hacer en DataEnvironment::Init y posiblemente tambin en el Init de su clase base Cursor. Para el nivel instanciado la configuracin de las propiedades no es confiable hasta que no se dispare el Form.Load.

Dnde ubicar los comandos SET que estn limitados a la Sesin privada de datos? Varios comandos SET de VFP estn limitados a la Sesin privada de datos. Muchos de esos comandos SET afectan los datos y por tanto necesitan ser ejecutados para cada sesin privada de datos. Adems, SET TALK est limitado a la sesin privada de datos y debe ser establecido lo antes posible, en OFF que es el valor no predeterminado para VFP, en el momento en que la sesin privada de datos (formulario) es creado, para eliminar las salidas TALK al _Screen o al formulario. Sin embargo, teniendo en cuenta las inconsistencias planteadas anteriormente en relacin con el objeto DataEnvironment, Cul es el mejor lugar para colocar los comandos SET en una sesin privada de datos, de forma tal que el formulario se cree correctamente? Eso depende de si utiliza o no DataEnvironment y en ese caso, si utiliza el DataEnvironment nativo de VFP o una clase personalizada DataEnvironment. No utilizar Dataenvironment Pienso que es la mejor eleccin, porque es ms consistente, y es ms fcil de mantener. En ese caso, lo ms lgico es colocar los comandos SET de la sesin privada de datos en el Load del formulario de la ms alta jerarqua que establezca la propiedad DataSession a 2 -Private Data Session. Comience el Load de cada creacin de formulario con llamada hacia atrs (callback), para asegurarse que los comandos SET quedarn configurados desde el inicio: IF NOT DODEFAULT() RETURN .F. ENDIF DataEnvironment nativo en un formulario basado en .SCX En el formulario de la ms alta jerarqua establezca la propiedad DataSession a 2- Private Data Session. Agregue un mtodo de usuario para colocar la configuracin lgica de los comandos SET para su sesin privada de datos junto a cualquier otra cosa que necesite que ocurra al inicio del todo de la ejecucin del formulario. En el nivel creado, llame al mtodo de usuario desde los mtodos del DataEnvironment OpenTables o, yo preferido, BeforeOpenTables: THISFORM.CustomMethod() Recuerde, cualquier cdigo que coloque en el mtodo de usuario en el nivel creado no se ejecutar, como fue explicado en la seccin anterior ... solamente se ejecuta el cdigo colocado en la(s) clase(s) padre de la instancia actual. DataEnvironment de usuario con DEClass/DEClassLibrary Coloque la configuracin lgica de los comandos SET para su sesin privada de datos en el Init de la clase DataEnvironment. Sea consciente de que pudiera tener algunos comandos SET como SET TALK OFF en el Init de sus clases base Cursor o Relation, ya que estos se ejecutan antes que el Init de DataEnvironment. Cmo establecer los comandos SET? Una vez que haya determinado dnde necesita colocar su cdigo para los comandos SET de su sesin privada de datos, la cuestin es cmo debe escribir ese cdigo. Puede escribir un cdigo estricto con los comandos SET deseados; pero el mejor enfoque pudiera ser tener la sesin privada de datos que lea los valores desde la sesin predeterminada de datos ... frecuentemente se utiliza la misma configuracin de comandos SET, estos se configuran globalmente para la sesin predeterminada de datos cuando inicia su aplicacin. Los ejemplos LISAG_PDS_SETs_Abstract*.PRGs y sus correspondientes .SCX demuestran una de estas tcnicas. Cada LISAG_PDS_SETs_Abstract*.PRG instancia un demo (objeto aplicacin) "application object", que contiene un mtodo de usuario GetSETCommandSetting(). Como el objeto application es instanciado en la sesin predeterminada de datos #1, al llamar a su mtodo GetSETCommandSetting() DEVUELVE la configuracin especificada como lo establece la sesin predeterminada de datos. Como la sesin privada de datos instancia, pueden establecer sus comandos SET para que coincidan con aquellos establecidos en la sesin predeterminada de datos llamado al objeto application global. Aqu est el cdigo esencial del mtodo SetSETCommands de la clase frmLISAG_PDS_SETs_Abstract en LISAG_PDS_SETs_Abstract.VCX:

LOCAL laSETs[3], luSetting, lcString laSets[1] = "DELETED" laSets[2] = "MULTILOCKS" laSets[3] = "TALK" FOR EACH lcSet IN laSETs luSetting = goApplication.GetSetCommandSetting(m.lcSet) lcString = "SET " + m.lcSet + SPACE(1) + TRANSFORM(m.luSetting) &lcString ENDFOR Utilice DataEnvironment slo en tiempo de diseo: Antes de que decida abandonar del todo el uso del DataEnvironment, existe una idea que debe considerar: Mientras est diseando formularios basados en .SCX, utilice DataEnvironment solo para cuestiones de diseo: Arrastrar y soltar un(os) cursor(es) al formulario para crear instantneamente controles Grid. Arrastrar y soltar los campos al formulario para agregar controles cuyos ControlSource ya estarn definidos y con un ancho aproximado (Width) (si el mapeo de campos (field mapping) tiene establecido que incluya el ttulo del campo, se puede obtener ya la etiqueta correspondiente al Caption existente). Establecer el ControlSource de cualquier control desde la ventana propiedades, seleccionando uno los cursores actuales desde el cuadro desplegable. Tener acceso al DataEnvironment y sus miembros (cursores) en los generadores de usuarios. Recuerde establecer las propiedades AutoOpenTables y AutoCloseTables a .F. como muestra la Figura 5, entonces VFP ignora todo lo que est en el DataEnvironment en tiempo de ejecucin. Sin embargo, para formularios con sesin privada de datos, cuando el formulario es cerrado/destruido, VFP cierra todos los cursores abiertos mientras estuvo activo el formulario. En tiempo de ejecucin abra las vistas y tablas manualmente, utilizando una de las tcnicas descritas en el mtodo Load de DesignTimeDE.SCX, aprovechando sus ventajas sobre el comportamiento del DataEnvironment en tiempo de ejecucin: Si/cuando hay un problema al abrir una tabla/vista, se puede enviar un mensaje de usuario y devolver (RETURN) .F., interrumpir la creacin de esa tabla/vista mientras contina intacto el resto de la aplicacin. Por el contrario, cuando DataEnvironment encuentra un problema como un archivo no encontrado, cabecera de tabla daada, ndice daado, etc. El DataEnvironment falla, se interrumpe su ejecucin, as como todo el resto de la aplicacin. Se puede establecer el comando SET PATH antes de llamar los datos, de esta forma se puede intercambiar entre diferentes conjuntos de datos o simplemente ajustar la ruta (PATH) antes de llamar los datos Puede utilizar herramientas como Stonefield Database Toolkit para reparar problemas con tablas, ndices, memos, etc. Utilizando esta tcnica, puede ignorar las inconsistencias por utilizar DataEnvironment, que se han descrito anteriormente en este documento, ya que no hace nada en tiempo de ejecucin. Incluso en un diseo n-Capas, si sus objetos de Negocio proporcionan un cursor de datos puede agregar tablas/vistas al DataEnvironment (establecer propiedad Alias), all donde estn disponibles para conveniencia en tiempo de diseo y son ignoradas en tiempo de ejecucin, cuando el objeto negocio proporciona el dato real. Esta tcnica trabaja igualmente bien para formularios basados en .VCX ... no existe objeto DataEnvironment nativo, con lo cual tiene que cargar los datos y cdigo desde el mtodo Load y por tanto ignorar el DataEnvironment. Si se establecen las propiedades DEClass/DEClassLibrary, los datos indicados estn disponibles en tiempo de ejecucin pero el DataEnvironment no est disponible en tiempo de diseo. Mucho cuidado al romper la secuencia nativa de eventos de creacin Existen vas para romper la secuencia nativa de eventos de creacin. Algunas veces las consecuencias son menos graves, en otras son catastrficas y pueden causar todo tipo de comportamiento indeseado. El ejemplo LISAG_SetFocus demuestra una de las posibilidades. Desafortunadamente esto es muy comn y muy fcil de hacer. La ltima lnea de cdigo en el evento Form.Init, es esta lnea aparentemente inocente que asegura que al crear, el botn OK tiene el foco: THIS.cmdOK.SetFocus()

Sin embargo ver que el orden de los eventos no es LISAG, sino LIAGIS como se muestra en la figura 7. Cuando el Init realiza el SetFocus, VFP tiene que activar inmediatamente el formulario y darle el foco, para que el botn OK pueda tener el foco en ese punto. Despus de esa lnea de cdigo, el Form.Init ejecuta cualquier cdigo restante despus de esa lnea y contina con el evento Show. No muchos formularios dan el SetFocus al primer control de esta forma ... podra simplemente establecer el comando OK primero en el orden de tabulacin. Lo que es ms comn es establecer el foco condicionalmente a un control en particular, basado en alguna accin del formulario, basado a se vez, en un parmetro que reside en el Init. Simplemente he omitido esta condicin en el ejemplo LISAG_SetFocus para demostrar lo que ocurre cuando se encuentra la condicin. Pero, Cul es el dao? Eso depende de qu ha codificado en los otros eventos de creacin del formulario que normalmente se ejecutan en orden nativo una vez que el Init haya terminado completamente. Por ejemplo, puede tener cdigo en lo eventos Show y Activate que se disparan solamente si se est ejecutando durante la creacin del formulario (es posible hacerlo por programa - Show() de un formulario en cualquier momento y el Activate se ejecuta cada vez que el formulario se convierte en formulario activo, por ejemplo cuando el usuario hace clic en un formulario no modal abierto en el escritorio VFP) con este fin, agregue una propiedad de usuario (una bandera) con valor predeterminado a .T. , y lo hace igual a .F. en el Activate o GotFocus. El cdigo a travs del proceso de creacin pudiera ejecutar condicionalmente slo si el formulario est siendo creado: IF THIS.lInstantiating * realizar estas acciones solo si THISFORM se est instanciando ENDIF Ahora, si existe Member.SetFocus(), en el Form.Init la bandera se establece prematuramente en .F., antes de que el Init finalice y antes de que se ejecute el Show. Cualquier cdigo en Form.Show que se debe ejecutar solamente durante la creacin ser ignorado porque la bandera ya est en .F. en un escenario normal (no una demo) es un error muy difcil de depurar porque solo ocurre si la condicin encuentra el Member.SetFocus() explcito. El ejemplo demuestra como establecer ese tipo de propiedad de usuario Cul es la solucin? Entonces, qu se puede hacer en esos casos donde se necesita establecer condicionalmente el foco a un control particular al crear un formulario? El ejemplo LISAG_SetFocus1 demuestra una tcnica. Adems de demostrar la idea de una propiedad lInstantiating, utilizada como bandera, aade un mtodo de usuario InitalSetFocus llamado desde Form.Activate slo durante la creacin. InitalSetFocus proporciona un lugar especfico para que el desarrollador ponga su cdigo para establecer el foco condicionalmente a un miembro en particular de un formulario ... sin romper la secuencia nativa de los eventos al instanciar. Cuando al llamar Form.Load() o Form.Init() devuelven .F. no se disparan los eventos Form.Destroy ni Form.Unload Devolviendo .F. desde el Load o el Init de un formulario no se crea, es evidente; pero lo que no se dijo: 1. Cuando el Load devuelve .F., no se disparan ni Form.Destroy ni Form.Unload. 2. Cuando el Init devuelve .F., se dispara Form.Unload; pero no lo hace Form.Destroy. Debido a que los miembros contenidos en el formulario se crean antes que el Form.Init, el Destroy de esos miembros se dispara ya que estn fuera de lmites. (Since form members instantiate before the Form.Init, the Destroy of form members does fires as they go out of scope). Bueno, y qu? Bueno, pues frecuentemente colocamos el cdigo de limpieza en el Form.Destroy, y puede tener cdigo abstracto en el evento Destroy en la clase base o en otras clases Form que estn en la jerarqua del creado actualmente. Lo mismo se puede cumplir para Form.Unload. El cdigo de limpieza del Cleanup no se ejecuta cuando el Form.Load o el Form.Init devuelven .F. y el cdigo de limpieza del Cleanup no se ejecuta si el Form.Load devuelve .F.! Ejecutar Form.Destroy consistentemente Los formularios LISAG_QRDU_AbortLoadInit1.SCX and LISAG_QRDU_AbortLoadInit2.SCX demuestran tcnicas similares que puede utilizar para garantizar que los Form.Destroy/Unload (cdigos de limpieza) se ejecuten adecuadamente.

Peculiaridad del SYS(1271) Primeramente vamos a mirar la funcin SYS(1271) que podemos poner a trabajar para nuestro beneficio. El ejemplo The LISAG_SYS1271.SCX demuestra que no puede hacer esta llamada: SYS(1271,THISFORM) Hasta que se haya completado Form.Load, no se puede llamar desde un mtodo de DataEnvionment, el Form.Load ni desde otro mtodo llamado desde el Form.Load. De hacerlo, recibir el mensaje, poco intuitivo, "Insuficiente memoria para completar esta operacin" ("Not enough memory to complete this operation"). Puede verlo al hacer DO FORM LISAG_SYS1271 ... seleccionar <Ignore> para continuar con la instalacin de LISAG_SYS1271.SCX. Por un lado, es inconveniente tener que esperar hasta el Form.Load para consultar SYS(1271,THISFORM) si desea realmente verificarlo antes. Pero LISAG_QRDU_AbortLoadInit1.SCX utiliza este comportamiento para determinar cmo debe ser invocado Form.Destroy. LISAG_QRDU_AbortLoadInit1.SCX Los eventos Destroy e Init de LISAG_QRDU_AbortLoadInit1.SCX contienen una instruccin IF .F., tal que si la cambia por IF .T., provoca que el mtodo devuelva .F. La tcnica demostrada en LISAG_QRDU_AbortLoadInit1.SCX es: 1. Al devolver .F. desde Init/Load debido a un failed callback, NO incluya una llamada manual al Form.Destroy, asumiendo que cada clase padre Form que devuelva .F. desde su cdigo abstracto lo har. 2. Al devolver .F. desde Init/Load debido a cdigo en ese nivel, llama THIS.Destroy() antes del Return .F., asegurndose de que cualquier cdigo de limpieza en el Destroy se va a ejecutar adecuadamente. 3. El evento Form.Destroy contiene cdigo para llamar a SYS(1271,THISFORM) para determinar si el Destroy ocurre normalmente (no existe error en SYS(1271)) o debido a llamarlo explcitamente desde el Form.Load (SYS(1271) genera un error). Al llamar manualmente desde Form.Load, es llamado el Form.UnLoad.

LISAG_QRDU_AbortLoadInit2.SCX Los eventos Destroy e Init de LISAG_QRDU_AbortLoadInit1.SCX contienen una instruccin IF .F., tal que si la cambia por IF .T., provoca que el mtodo devuelva .F. La tcnica demostrada en LISAG_QRDU_AbortLoadInit2.SCX es: 1. Al devolver .F. desde Init/Load debido a un failed callback, NO incluya una llamada manual al Form.Destroy, asumiendo que cada clase padre form que devuelva .F. desde su cdigo abstracto lo har. (igual que LISAG_QRDU_AbortLoadInit1.SCX) 2. Al devolver .F. desde Init/Load debido a cdigo en ese nivel, llama THIS.Destroy() antes del Return .F., asegurndose de que cualquier cdigo de limpieza en el Destroy se va a ejecutar adecuadamente. (igual que LISAG_QRDU_AbortLoadInit1.SCX) 3. El Form.Destroy contiene cdigo para verificar PROGRAM(PROGRAM(-1)-1) para determinar si el Destroy se ha llamado manualmente desde el Form.Load y, en tal caso, llama al Form.Unload. Referencia de objetos en la destruccin de formularios. (Object Reference Cleanup On Form Destruction) Para que cada objeto se libere / destruya adecuadamente, todas las referencias externas a sus miembros deben ser liberadas. Esto significa que para cerrar/destruir un formulario, cualquier objeto externo que mantenga referencia a uno o ms miembros debe liberarse explcitamente esa referencia o establecerse igual a .NULL. Si algunas de las referencias de objetos no se liberan explcitamente, el contenedor no se libera. Si el contenedor es un formulario o un contenedor dentro de un formulario, el formulario no se libera. Esta es la causa del error "Referencia de objeto daada" ("Dangling object reference"). ORCleanup1.PRG crea 2 instancias de ORCleanup1.SCX para demostrar el problema: 1. DO ORCleanup1 2. En la instancia 2 del formulario, seleccione cualquier casilla de verificacin del grupo inferior para guardar una referencia de objeto a un miembro de la instancia1 del formulario.

3. Intente cerrar la instancia 1: Haga clic en OK, haga clic en "X" a la derecha de la barra de ttulo, o seleccione Close desde el men en la caja de control de la barra de ttulo. La instancia 1 del formulario se niega a cerrarse, debido a referencias de objeto daadas, de hecho, si selecciona Cerrar desde el men en la caja de control de la barra de ttulo, la opcin Cerrar no aparece y la "X" a la derecha de la barra de ttulo aparece deshabilitada. 4. Cierre la instancia 2 del formulario. En cuanto se cierra, se cierra tambin la instancia1 ... su cdigo Destroy ya se haba disparado, solo estaba esperando a que se liberaran las referencias de objetos externos a sus miembros. Lo que no te dijo tu madre Para formularios, la limpieza de referencia de objetos nunca se debe hacer despus de Form.Destroy. Esto es fcil de hacer. Sin embargo, cuando los miembros del formulario necesitan limpiar las referencias de objetos, el cdigo colocado en su Destroy puede ser intil. Recuerde: el formulario destruye "de afuera hacia adentro", por tanto el Destroy de los miembros se dispara despus del Destroy del formulario como tal. Cuando se daa la referencia de objetos existente, se dispara el Form.Destroy; pero la destruccin frena aqu, y no se dispara ningn otro evento, incluyendo el Destroy de sus miembros, hasta que se libera la referencia de objeto daada. Esto es un gran problema cuando disea formularios de tal forma que un formulario no modal mantenga referencias de objetos a un miembro de otro formulario no modal u otros objetos externos. Dos casos comunes de dao de referencia de objetos: 1. Uno o ms miembros de Form1 contienen referencias de objeto a miembros de Form2. El usuario intenta cerrar Form2; pero se niega a cerrar hasta que los miembros de Form1 liberan sus referencias de objeto (este es el escenario que se muestra en ORCleanup1.PRG/.SCX). 2. Uno o ms miembros de Form1 contienen referencias de objeto a miembros de Form2. El usuario cierra Form1; pero Form1 no se cierra del todo, queda como un objeto artificial como una sesin de datos "desconocida" ("Unknown") visible en la ventana Sesin de datos. Cuando objetos externos contienen referencias a miembros de THISFORM ORCleanup1a.SCX demuestra una va para solucionar el comportamiento necesario. Repita los pasos para ORCleanup1 y observe la diferencia: 1. DO ORCleanup1a. 2. En la instancia2 del formulario, seleccione cualquier casilla de verificacin del grupo inferior para guardar una referencia de objeto a un miembro de la instancia1 del formulario. 3. Cierre la instancia 1del formulario. Se cierra normalmente, como se espera, an cuando aparentemente no se ha hecho la limpieza de referencia de objetos para liberar las referencias de la instancia 2 del formulario tiene a los objetos de la instancia 1 del formulario. Puede ver la tcnica que he utilizado en ORCleanup1a.SCX para examinar el cdigo en el evento Clic de cada casilla de verificacin del grupo inferior. He utilizado la funcin BINDEVENT() para asegurar que cuando se guarda una referencia de objeto, el Destroy de los objetos guardados del formulario llama automticamente al cdigo de limpieza de los objetos haciendo su almacenaje. Entonces, cuando es cerrado el formulario cuyas referencias a objetos miembros han sido guardadas, su Destroy llama al cdigo de limpieza de la referencia de objetos de los objetos externos manteniendo la referencia de objetos. He aqu lo que ocurre: 1. DO ORCleanup1a 2. Haga Clic en la primera casilla de verificacin del grupo inferior de la instancia 2 del formulario. El cdigo del evento Clic guarda una referencia de objeto al miembro txtDemo1 en la instancia 1 del formulario a la propiedad de usuario oFormMember de la instancia 2 del formulario. El cdigo Clic tambin ejecuta BINDEVENT() para asegurarse de que cuando se cierra la instancia 1 del formulario se ejecuta el mtodo ORCleanup de la instancia 2 del formulario. 3. Haga Clic en el botn de comandos OK de la instancia1 del formulario. Cuando se dispara el Destroy, ejecuta el mtodo Cleanup de la segunda instancia del formulario, gracias al BINDEVENT(). Entre otras cosas, el mtodo ORCleanup de la instancia 2 del formulario establece su propiedad oFormMember a .NULL., liberando la referencia de objeto guardada el txtDemo miembro de la instancia 1 del formulario. En un conjunto real de clases jerrquicas, pudiera probablemente agregar el mtodo ORCleanup a cada una de sus clases bases, de tal forma que el Checkbox.Click hara BINDEVENT() a su propio mtodo ORCleanup en lugar de THISFORM.ORCleanup, haciendo ms granular el control del proceso. Esta tcnica requiere, por supuesto, utilizar VFP 8.0, versin en la que fue agregada al lenguaje la poderosa funcin BINDEVENT().

Cuando los miembros de THISFORM contienen referencias a objetos externos Como se ha descrito antes, toda la limpieza a las referencias de objeto "garbage collection" debe hacerse antes de Form.Destroy, que ocurre antes que se dispare el evento Destroy de cualquier miembro. La solucin es codificar el Form.Destroy con mensajes a sus miembros para establecer explcitamente todas las referencias de objeto guardadas a .NULL. Mi sugerencia es separar esta tarea a un mtodo de usuario ORCleanup agregado a sus clases base. Primero, agregue un mtodo de usuario ORCleanup agregado a sus clases base Form. Agregue cdigo al Destroy de su clase base formulario para llamar a THISFORM.ORCleanup(). En cada clase formulario o instancia, cada vez que escriba cdigo para guardar una referencia de objeto a un miembro de formulario, asegrese de colocar el cdigo de liberacin correspondiente en el ORCleanup del formulario contenido. Cada vez que se dispara el Form.Destroy, es realizada la limpieza a todas las referencias de objeto. Sin embargo, puede que quiera abstraerse en lo delante de este comportamiento, ya que cuando est diseando la clase contenedora (pageframes, optiongroups, grids, containers, etc.), no puede colocar cdigo en el Destroy del formulario contenido porque no sabe qu instancia del formulario o clase va a contener finalmente el contenedor que est preparando. Para solucionar este problema, agregue un mtodo de usuario ORCleanup para cada una de sus clases base que pueden ser miembros de un formulario (Textbox, Spinner, Custom, etc.). Programe su clase base form Form.ORCleanup para interactuar con cada uno de sus miembros, llamando su mtodo ORCleanup. Si disea el mtodo ORCleanup de objetos contenedores (grids, pageframes, pages, optiongroups, containers, etc.) para interactuar con cada uno de sus miembros de la misma forma que el ORCleanup a nivel de formulario solo hace un lazo entre su arreglo de controles y llama al ORCleanup de cada uno de sus miembros directos. Daar la referencia de objetos cuando se utilizan colecciones Ver la serie de ejemplos ORCleanup2.SCX _Screen.ActiveForm y qu se puede hacer con esto Durante el curso de creacin del formulario hay un punto en el cual el formulario se convierte en _Screen.ActiveForm. Sabiendo cuando esto ocurre nos permite poder hacer cosas muy buenas con la referencia de objeto _Screen.ActiveForm. Cuando THISFORM se convierte en _Screen.ActiveForm Como se demuestra en el ejemplo _ScreenActiveForm.SCX, el formulario que se est instanciando se convierte en _Screen.ActiveForm inmediatamente despus de ser mostrados (Show()) Lo que nunca te dijo tu madre Desafortunadamente, _Screen.ActiveForm no es siempre _Screen.ActiveForm. En muchas ocasiones donde el formulario activo contiene uno o ms controles ActiveX, _Screen.ActiveForm puede ser una referencia de objeto a un control ActiveX, NO al formulario que lo contiene. La biblioteca en tiempo de ejecucin X6SAF.PRG para el framework Visual MaxFrame Profesional est incluida en esta sesin y controla este caso. Para tener una referencia de objeto confiable _Screen.ActiveForm, sustituya el cdigo por el siguiente: LOCAL loActiveForm loActiveForm = _Screen.ActiveForm IF TYPE("loActiveForm.BaseClass") = "C" * aqu no es el formulario activo ELSE * m.loActiveForm contiene una referencia de objeto fiable al formulario actualmente activo ENDIF lo que es el equivalente ms fiable: LOCAL loActiveForm

loActiveForm = X6SAF() IF ISNULL(m.loActiveForm) * aqu no es el formulario activo ELSE * m.loActiveForm contiene una referencia de objeto fiable al formulario actualmente activo ENDIF Guardar una referencia al formulario y objeto llamados Al combinar los conocimientos sobre la secuencia de eventos de creacin de formulario LISAG y el conocimiento de cuando THISFORM se convierte en _Screen.ActiveForm, hay algunas cosas interesantes que puede hacer con esta informacin. El ejemplo _SAF1.SCX/_ScreenActiveForm1.SCX demuestra una buena tcnica para guardar fcilmente una referencia de objeto al formulario y objeto (si existe) llamados. Guardar una referencia al formulario llamado Cuando se ejecuta Form.Load, THISFORM no se ha instanciado todava, y no es el formulario activo _Screen.ActiveForm. Ms importante, cualquier formulario (si existe) que se ejecuta cuando THISFORM se llama se refleja aun en _Screen.ActiveForm. As, es como un pestaeo a obtener una referencia de objeto "libre" al formulario que se est ejecutando cuando se llama THISFORM, y lo guarda en una propiedad de usuario disponible durante la vida del THISFORM: LOCAL loActiveForm loActiveForm = X6SAF() IF VARTYPE(m.loActiveForm) = "O" THIS.oCallingForm = m.loActiveForm ELSE THIS.oCallingForm = .NULL. ENDIF A partir de ahora, THISFORM, puede "hablar" al formulario llamado via la referencia de objeto THISFORM.oCallingForm object. THISFORM.Destroy establece en .NULL. THISFORM.oCallingForm object: Algunas cosas interesantes sobre estas tcnicas: 1. Puede consultar informacin sobre el formulario llamado antes del THISFORM.Init, donde se reciben parmetros, aceptando otra configuracin que necesita optimizar lo que ocurre antes de que puedan ser verificados los parmetros. 2. No hay necesidad de llamar al formulario para pasar una referencia de objeto a s mismo al formulario llamado. 3. Cuando THISFORM se instancia desde cualquier sitio, como un men, no hay sitio para el formulario activo para pasar una referencia de objeto a si mismo a THISFORM.Init 4. Debido a que la referencia de objeto para el formulario llamado se guarda en una propiedad de usuario de THISFORM, est disponible durante la vida de THISFORM, luego de _Screen.ActiveForm se actualiza nunca despus de la referencia del formulario llamado. Estas caractersticas se muestran en el ejemplo _SAF1.SCX/_ScreenActiveForm1.SCX como se muestra en la figura 11: 1. El evento Clic del cmdCallForm en _SAF1.SCX un comando DO FORM ... no se pasan parmetros. 2. El evento Load del _ScreenActiveForm1.SCX contiene cdigo para verificar el formulario llamado, y entonces, guarda una referencia de objeto a su THISFORM.oCallingForm. 3. El evento Init del grdCustomers en _ScreenActiveForm1.SCX contiene cdigo para verificar el formulario llamado. En tal caso este formulario es consultado por su actual Orders.CustomerID, en cualquier caso, como se muestra en la figura 11. En ese caso grdCustomers establece el puntero a su registro inicial al CustomerID indicado. Observe que esta aplicacin tiene lugar antes que THISFORM.Init, donde un parmetro CustomerID pudiera ser recibido y aplicado. Puede DO FORM _ScreenActiveForm1.SCX con nada, y el cdigo descrito arriba simplemente encuentra que no hay mucho que puedas hacer. Abstraerlo!

El comportamiento que ve en Load, Destroy, y ORCleanup puede abstraerse simplemente en su formulario base clase. Todos los formularios que heredarn esta caracterstica cada instancia lo utilicen o no. Guardar una referencia a un control del formulario llamado Al guardar una referencia de objeto de un formulario existente es fcil. Pero, qu tal si guardamos una referencia de objeto al control actual en el formulario; pero slo si este control est en la pila de ejecucin del programa, indicando que es responsable por llamar THISFORM (como el botn cmdCallForm en _SAF1.SCX)? Esto toma un poco ms de trabajo, puede fcilmente abstraerse de tal forma que est disponible para todos los formularios _ScreenActiveForm1.SCX contiene el cdigo necesario: 1. Load contiene un cdigo que verifica la pila de ejecucin del programa para la llamada del control en el formulario llamado, si se encuentra uno, se guarda una referencia en THISFORM.oCallingFormControl para utilizarlo mientras exista THISFORM. 2. Como se ha explicado previamente en este documento, en cualquier momento puede guardar una referencia de objeto para un miembro de objeto, debe proporcionar para la limpieza de la referencia de objeto. El Load de _ScreenActiveForm1.SCX hace que BINDEVENT() enlaza el Destroy del formulario llamado con THISFORM.ORCleanup. Si _ScreenActiveForm1.SCX es modal, esta accin no se requiere actualmente, porque THISFORM se puede cerrar antes de que el formulario puede ser llamado. 3. El Destroy de _ScreenActiveForm1.SCX llama a su ORCleanup de usuario. 4. El ORCleanup de_ScreenActiveForm1.SCX libera explcitamente las referencias de objeto oCallingForm y oCallingFormControl. Cuando THISFORM es no modal, se dispara el Destroy del formulario llamado THISFORM.ORCleanup gracias al BINDEVENT() en THISFORM.Load Observe que la referencia de objeto del formulario llamado, si este control es realmente llamado por el formulario llamado, se guarda slo en THISFORM.oCallingFormControl. Por ejemplo, si se ejecuta un formulario cuando se llama un segundo formulario desde otro lado como una opcin de men, el control activo en el formulario activo no llama al segundo formulario. THISFORM.oCallingFormControl no se guarda debido a que un mtodo del control activo en el formulario activo no est en la pila de ejecucin del programa. Entonces, como se ha explicado en el comentario del cdigo al final del mtodo Load de _ScreenActiveForm1.SCX, puede decirlo fcilmente si THISFORM.oCallingForm es llamado por THISFORM, o fue simplemente el formulario llamado cuando es llamado THISFORM. Estas caractersticas se demuestran en el ejemplo _SAF1.SCX/_ScreenActiveForm1.SCX, como muestra la Figura 12: 1. El Init de _ScreenActiveForm1.SCX determina sus posiciones Top y Left relativos al control llamado, si existe. Las referencias de objeto a un formulario llamado y su control llamado podra pasar al Init, en lugar de utilizar la referencia de objeto THIS.oCallingFormControl guardada en Load. Sin embargo, esto requiere que el desarrollador que codifica la llamada al formulario recordando que pase siempre los parmetros necesarios a _ScreenActiveForm1.SCX, y en el orden correcto. 2. El AfterRowColChange de grdCustomers en _ScreenActiveForm1.SCX actualiza el Caption del botn del formulario llamado. Esto es solamente por propsitos de diversin / demo, para mostrar cual fcil es "hablar" al control del formulario llamado. 3. El ORCleanup de _ScreenActiveForm1.SCX contiene cdigo para cambiar el Caption del botn del formulario llamado. Puede adems hacer DO FORM _ScreenActiveForm1 con nada, y el cdigo escrito antes encuentra que no hay mucho que hacer. Abstraerlo! El comportamiento que ve en el Load, Destroy, y ORCleanup se puede abstraer fcilmente en su clase base formulario. Todos los formularios heredan esta caracterstica aunque la utilice o no la instancia indicada. Mantener referencias de objetos a los formularios llamados El ejemplo _SAF1.SCX/_ScreenActiveForm1.SCX descrito en la seccin previa pudiera hacerle pensar sobre una tcnica ms poderosa. En ese caso, seguramente disfrutar de esta. Me han preguntado por un cdigo para situaciones donde el formulario no modal necesite no slo llamar a uno o ms formularios no modales adicionales; pero adems para cada uno de los formularios llamados para mantener las referencias de objeto a cada otro as que puede actualizar cada otro de tiempo en tiempo. Con ms frecuencia me han preguntado algunos desarrolladores que han tratado de implementar una situacin, como para depurarlo. No es trivial, mantener todas las referencias de objeto en ambas direcciones, ni para asegurarse que esa limpieza de referencia de objeto (garbage collection) fue hecha para prevenir una posibilidad de daar la referencia de objeto.

Gracias a la funcin BINDEVENT() agregada en VFP 8.0, este escenario es ahora muy fcil de implementar. Mejor an, el cdigo necesario es fcil de conceptuar. La serie de ejemplos FormORCleanupCaller*.SCX contienen el cdigo. Todos los formularios en la serie FormORCleanupCaller*.SCX estn basados en el ejemplo de la clase base formulario frmBaseForm en la biblioteca FormORCleanup.VCX. La tcnica es manipular todo sin pasar ningn parmetro, utilizando una tcnica similar demostrada en el ejemplo _SAF1.SCX/_ScreenActiveForm1.SCX. Aqu vemos los conceptos en frmBaseForm: 1. La llamada desde Load llama al mtodo de usuario StoreCalledForm. 2. La llamada desde StoreCalledForm verifica si existe un formulario llamado. En ese caso:<ol type="a"> 3. La referencia de objeto a un formulario llamado desde cualquier objeto llamado es guardada en THISFORM.oCallingForm y THISFORM.oCallingFormControl. 4. Un BINDEVENT() es utilizado para asegurar que cuando se cierra el formulario llamado, es llamadoTHISFORM.ORCleanupCallingForm, donde las referencias de objeto se guardan en THISFORM.oCallingForm y son liberados THISFORM.oCallingFormControl, permitiendo que el formulario llamado cierre adecuadamente. 5. Si el formulario llamado tiene un mtodo de usuario StoreCalledForm (ser si hereda desde frmBaseForm), es llamado su mtodo StoreCalledForm y se pasa una referencia de objeto a THISFORM. La llamada desde StoreCalledForm:<ol type="a"> Guarda una referencia de objeto al formulario llamado en una propiedad de arreglo (Tuve problemas de hacer una propiedad coleccin para trabajar adecuadamente). Utilice un BINDEVENT() para asegurar que cuando se cierra el formulario llamado (el que est actualmente bajo creacin), el mtodo ORCleanupCalledForm del formulario llamado es llamado, donde es liberada la referencia de objeto al formulario llamado. El resultado en cadena es que ambas llamadas y llamados formularios mantengan la referencia de objetos entre ellos. Cada formulario llamado puede tener una referencia de objeto a slo un formulario invocador; pero cada formulario invocador puede mantener la referencia a un nmero ilimitado de formularios invocados. Ms importante, es manipulada la necesaria limpieza de referencia de objeto. Cada instancia de formulario hereda todo el comportamiento necesario, y el formulario o sus miembros puede simplemente THISFORM., THISFORM.oCallingFormObject, y THISFORM.aCalledForms[] objetos en cualquier momento (por supuesto, despus de verificar para ver si son referencias vlidas de objeto) Este comportamiento se demuestra de esta forma, como se observa en las figuras 14 y 15: 1. DO FormORCleanupCaller1.SCX 2. Haga Clic en cualquiera de los botones DO FORM. Si hace Clic en alguno de ellos ms de una vez, se cargan nuevas instancias de FormORCleanupCalled*.SCX sobre las ya existentes. 3. Haga Clic en el botn <?> de cualquier formulario para ver en cada formulario sobre el otro invocador/invocado. 4. Haga Clic en el botn <OK> de cualquier formulario para que vea que no hay problemas de referencias de objetos daadas. FormORCleanupCaller2.SCX El ejemplo FormORCleanupCaller2.SCX es el mismo que FormORCleanupCaller1.SCX, excepto que cuando el formulario invocador es cerrado, todos los formulario que llama se cierran automticamente. Encontrar el cdigo para esto en el evento Destroy. Nota: El autor ha dado su autorizacin, y los ejemplos se pueden descargar de: DrewSpeedieDemo.zip (167 KB).

Lo que nunca te cont tu madre sobre instanciar y destruir formularios :: PortalFox :: Nada corre c...

Pgina 1 de 9

Hola invitado

15 Jun, 2013 - 01:02

Men principal
Inicio Temas Secciones Descargas Enlaces

Lo que nunca te cont tu madre sobre instanciar y destruir formularios


(7606 palabras totales en este texto) (22009 lecturas)

Indice Conferencias MasFoxPro Enviar noticia Bsquedas Usuarios Preferencias Top 10 P+F [FAQ] RSS

Lo que nunca te cont tu madre sobre instanciar y destruir formularios


Autor: Drew Speedie (www.visionds.com) Traducido por: Ana Mara Bisb York (amby@telefonica.net) Para: PortalFox (www.portalfox.com) (Sesin What Your Mother Never Told You About Form Instantiation and Destruction presentada por el autor en la Conferencia DevEssentials Kansas, 2004)
Nota aclaratoria de la traductora

Anuncios

Anuncios Google

El artculo publicado inicialmente tena un error en su contenido, al explicar los eventos que se suceden al destruir formularios. Consultado el autor, el artculo fue corregido el 13 de Abril de 2005. (ver noticia Ms sobre "Lo que tu madre no te cont sobre instanciar y destruir Formularios"). Asimismo el autor autoriz la descarga de los ejemplos. Los ejemplos citados se pueden descargar de: DrewSpeedieDemo.zip (167 KB). Resumen Esta sesin intenta ayudar a entender mejor la secuencia normal de eventos en VFP cuando los formularios se instancian y se destruyen ... existe mucho ms los eventos Init y Destroy. Dotado de este conocimiento, puede depurar problemas e implementar buenas tcnicas como las que vamos a demostrar aqu. Todos los ejemplos se pueden probar en VFP 8 o VFP 9, porque no se utiliza cdigo especfico para VFP 9. La mayora de los ejemplos se aplican a todas las versiones de VFP; pero algunos de los ejemplos utilizan las funciones BINDEVENT()s y las caractersticas de la clase DataEnvironment que fueron agregadas en VFP 8. La mayora de los ejemplos se pueden ejecutar desde la interfaz DEMO.APP, aunque algunos deben comenzar con CLEAR ALL/CLOSE ALL, y deben ejecutarse desde la ventana de comandos. DEMO.APP es el nico archivo incluido con la presentacin. Una vez que selecciona los botones Run (Ejecutar) o Source (Fuente) desde la interfaz, los archivos con cdigo fuente para ese ejemplo se copian al disco, en la carpeta donde se encuentra DEMO.APP. A partir de ah, puede ejecutar los ejemplos desde la ventana de comandos. La mayora pueden ser ejecutados directamente desde la interfaz DEMO.APP. Las bases LISAG (Load-Init-Show-Activate-GotFocus) y QRDU (QueryUnload- Release- Destroy- Unload) Para implementar exitosamente los escenarios de instanciacin y destruccin de un formulario, lo primero que debe entender es la secuencia nativa de los eventos. Instanciacin Como se demuestra en LISAG_QRDU.SCX, aqu est la lista de eventos que ocurren durante la instanciacin: 1. Evento Form.DataEnvironment.OpenTables (Tablas/vistas en el Entorno de datos DataEnvironment no estn en uso USE() o abiertas) 2. Evento Form.DataEnvironment.BeforeOpenTables (Tablas/vistas en el Entorno de datos DataEnvironment no estn en uso USE() o abiertas) 3. Evento Form.Load (Tablas/vistas en el Entorno de datos DataEnvironment estn en uso USE() o abiertas) 4. Evento Init de cualquier objeto cursor en DataEnvironment 5. Evento Form.DataEnvironment.Init 6. Evento Init de cada miembro del formulario que es instanciado 7. Evento Form.Init 8. Evento Form.Show 9. Evento Form.Activate 10. Evento When del primer control del formulario en el orden de tabulacin (tab order) 11. Evento Form.GotFocus 12. Evento GotFocus del primer control del formulario en el orden de tabulacin Destruccin Como se demuestra en LISAG_QRDU.SCX, aqu est la lista de eventos que ocurren durante la destruccin cuando el formulario es cerrado por el usuario haciendo Clic en la "X" en la esquina superior derecha de la barra de ttulo del formulario ... o ... por el usuario, seleccionando la opcin Cerrar desde el ControlBox en la esquina superior izquierda de la barra de ttulo del formulario: 1. 2. 3. 4. Evento Evento Evento Evento Form.QueryUnload Form.Destroy Destroy para cada uno de los miembros del formulario. Form.Unload (Tablas/vistas en el Entorno de datos DataEnvironment estn en uso USE() o abiertas)

VFP Foxpro Foxpro Fox pro

http://www.portalfox.com/index.php?name=Sections&req=viewarticle&artid=42&page=1

15-06-2013

Lo que nunca te cont tu madre sobre instanciar y destruir formularios :: PortalFox :: Nada corre c...
2012 PortalFox

Pgina 2 de 9

5. Evento Form.DataEnvironment.CloseTables (Tablas/vistas en el Entorno de datos DataEnvironment no estn en uso USE() o abiertas) 6. Evento Form.DataEnvironment.Destroy 7. Evento Destroy para cada cursor en el DataEnvironment Como se demuestra en LISAG_QRDU.SCX, aqu est la lista de eventos que ocurren durante la destruccin cuando el formulario es cerrado por una llamada a THISFORM.Release(), por ejemplo, al hacer Clic en un botn Aceptar 1. 2. 3. 4. 5. Evento Form.Release Evento Form.Destroy Evento Destroy para cada uno de los miembros del formulario. Evento Form.Unload (Tablas/vistas en el Entorno de datos DataEnvironment estn en uso USE() o abiertas) Evento Form.DataEnvironment.CloseTables (Tablas/vistas en el Entorno de datos DataEnvironment no estn en uso USE() o abiertas) 6. Evento Form.DataEnvironment.Destroy 7. Evento Destroy para cada cursor en el DataEnvironment

Instanciacin / Destruccin de miembros contenedores Como demostrar posteriormente el ejemplo LISAG_QRDU2.SCX, los contenedores se instancian "de dentro hacia afuera" de igual forma que hace el propio formulario (debido a que tambin es un contenedor) ... El Init de los miembros contenidos de disparan antes que el Init del contenedor padre. En la destruccin ocurre lo contrario ... el Destroy del contenedor se dispara antes que el Destroy de sus miembros contenidos. Cuando el formulario tiene establecido valores para DEClass, DEClassLibrary Como muestra el ejemplo LISAG_QRDU_DE.SCX, cuando un formulario establece valores para DEClass, DEClassLibrary (disponibles a partir de VFP 8.0), las cosas son ligeramente diferentes. Debido a que el objeto DataEnvironment es un objeto completamente separado, se instancia completamente antes del Form.Load. El Init de los miembros del DataEnvironment: Cursor, CursorAdapter, y Relation se disparan antes que el Init del DataEnvironment, siguiendo el comportamiento nativo de VFP donde el Init de los miembros ocurre antes que el Init del contenedor padre. El evento Destroy del DataEnvironment ocurre antes que el Destroy de sus miembros. Lo que nunca te cont tu madre Los ejemplos LISAG_QRDU*.SCX demuestran algunos aspectos de inters, muchos de los cuales pudiera no encontrar intuitivos: Form 1. En dependencia de cmo se cierra el formulario, se ejecuta el mtodo Release o el evento QueryUnload; pero no ambos ... Form.Release no es un buen lugar para colocar cdigo, no importa lo que sea, que se deba ejecuta cada vez que se destruya el formulario. Utilice en su lugar los eventos Destroy o Unload. 2. Debido a que muchos comandos como SET TALK estn limitados a la sesin privada de datos (ver DATASESSION TO en la ayuda de VFP para ver la lista de estos comandos SET), decidir en que lugar va a asignar valores a estos comandos SET depende de cmo abre sus datos. Si no utiliza nunca DataEnvironment es fcil: los comandos SET para la sesin privada de datos se colocan en el Load de la clase form. Si utiliza DataEnvironment nativo en un formulario basado en .SCX, tendr que establecer estos comandos SET en DataEnvironment.OpenTables/BeforeOpenTables, antes de que las tablas/cursores sean abiertos. Si implementa un DataEnvironment de usuario especificado en las propiedades DEClass/DEClassLibrary (VFP 8.0 y superior), podr establecer los comandos SET para la sesin privada de datos en el mtodo Init de la clase DataEnvironment; pero sea consciente, que el Init de los miembros Cursor, CursorAdapter, y los objetos Relation se disparan antes que DataEnvironment.Init. Vea adems un par de secciones a continuacin en este documento. 3. Al destruir un formulario no se disparan los eventos Form.Deactivate ni Form.LostFocus DataEnvironment 1. El DataEnvironment abre sus datos implcitamente entre el DataEnvironment.BeforeOpenTables y el Form.Load() ... inmediatamente antes del Form.Load. 2. El DataEnvironment NO abre los datos en el evento OpenTable -- OpenTables es de uso opcional para abrir programaticamente los datos si se utiliza AutoOpenTables = .F. 3. El evento BeforeOpenTables no se dispara antes (Before) que el evento OpenTables, BeforeOpenTables est mal nombrado y se debi llamar algo como AfterOpenTablesBeforeImplicitOpenTables. (DespuesAbrirTablasAntesImplicitamenteAbrirTablas) 4. El DataEnvironment.Cursor.Init se dispara DESPUS que el cursor ya fue abierto y DESPUS del Form.Load 5. El DataEnvironment.Init se dispara DESPUS que el DataEnvironment halla cumplido todas las misiones encomendadas. DESPUS que los cursores han sido puestos en uso, y DESPUS del Form.Load. Excepto cuando el DataEnvironment es una instancia de usuario especificada en las propiedades DEClass/DEClassLibrary, en ese caso se instancia completamente antes de Form.Load. 6. Consecuente con el comportamiento de OpenTables y cuando las tablas se abren explcitamente por el DataEnvironment, el mismo cierra las tablas ANTES del mtodo CloseTables. 7. Mientras la secuencia de instanciacin / destruccin para el formulario y sus miembros es consistente, la secuencia de instanciacin / destruccin para el DataEnvironment depende de su implementacin: DataEnvironment nativo para un formulario basado en .SCX, DEClass DataEnvironment de usuario para un formulario basado en .SCX o .VCX. Llamar THISFORM.Metodos o consultar THISFORM.Propiedades desde el DataEnvironment Los ejemplos LISAG_DE*.SCX demuestran comportamiento inconsistente relacionado con llamar a mtodos del formulario (THISFORM) desde el DataEnvironment. Lo que nunca te dijo tu madre DataEnvironment nativo de VFP para un formulario (.SCX) Cuando un formulario basado en .SCX utiliza DataEnvironment nativo de VFP, el DataEnvironment se instancia primero que el formulario como tal. Ver los ejemplos LISAG_QRDU*.SCX, los que demuestran el su comportamiento nativo. Sin embargo 1. LISAG_DE.SCX demuestra que las llamadas a mtodos de formulario (nativos o de usuario), desde mtodos del DataEnvironment que se disparan antes del Form.Load son COMPLETAMENTE IGNORADAS!. Por ejemplo, las llamadas a THISFORM.Metodos desde los eventos de DataEnvironment OpenTables y BeforeOpenTables no hacen nada, no se invoca ningn mtodo y no se genera un error. Supongo que VFP no ha iniciado todava la instanciacin del formulario y por tanto, no reconoce THISFORM como un objeto; pero yo debera esperar que se generara un error, como hace SYS(1271,THISFORM).

http://www.portalfox.com/index.php?name=Sections&req=viewarticle&artid=42&page=1

15-06-2013

Lo que nunca te cont tu madre sobre instanciar y destruir formularios :: PortalFox :: Nada corre c...

Pgina 3 de 9

2. LISAG_DE2.SCX demuestra que las llamadas a mtodos de formulario (nativos o de usuario), desde mtodos del DataEnvironment que se disparan antes del Form.Load S disparan esos mtodos EN CASO que hayan sido definidos en la clase Form a partir de la cual hereda el formulario actual. Por supuesto, todos los mtodos nativos de VFP heredan de la clase base Form de VFP. Entonces, el cdigo que se ejecuta es el heredado de la clase Form, NO cualquier cdigo ubicado en los mtodos nativos o de usuarios del formulario instanciado. Una vez que se dispara el Form.Load, la llamada a estos mismos mtodos disparan los mtodos para el nivel instanciado. 3. De igual forma, si en el DataEnvironment que se dispara antes del Form.Load, consulta el valor de una propiedad (nativa o de usuario) que se establece explcitamente en la ficha Propiedades a nivel del formulario instanciado, los valores son los que sean predeterminados por VFP para esa propiedad (.F. para todas las propiedades de usuario), como si hubiera establecido las propiedades como predeterminadas en la ficha propiedades. Sin embargo; para las propiedades establecidas en la ventana propiedades de cualquier clase padre del formulario instanciado, el valor es evaluado adecuadamente, tal y como esperamos. Sin embargo, puede establecer una propiedad en cualquier mtodo del DataEnvironment. 4. En los mtodos de DataEnvironment, IntelliSense no muestra NINGUN PEM (Propiedades, Eventos, Mtodos) de usuario, ya sea de la instancia actual o de alguna clase padre de la instancia actual del formulario. Conclusin La conclusin es que se puede abstraer del comportamiento de DataEnvironment para mtodos de usuario de formulario, ya que estos mtodos estn basados en formularios .SCX. Y debe recordar que el cdigo que coloque en estos mtodos en el nivel instanciado del .SCX ser completamente ignorado, cuando esos mtodos son llamados desde eventos del DataEnvironment como OpenTables y BeforeOpenTables que se ejecutan antes que Form.Load. Para el nivel instanciado establecer propiedades es poco fiable hasta que no se ejecute Form.Load. DataEnvironment con DEClass/DEClassLibrary de usuario para un formulario basado en .SCX Cuando las propiedades DEClass y DEClassLibrary de un formulario basado en .SCX tienen asignado un valor que especifique un objeto DataEnvironment de usuario, las cosas son algo diferentes 1. En tiempo de diseo, cuando da valor a las propiedades DEClass y DEClassLibrary asignando una clase DataEnvironment con cdigo en su evento Init, o en el Init de alguno de sus miembros cursor/relation, y ese Init llama a un mtodo de usuario de ese formulario, slo el hecho de asignar valor a las propiedades DEClass y DEClassLibrary genera un error "Objeto no contenido en el formulario" ("Object is not contained in a Form"), para cada llamada al mtodo de usuario del formulario (THISFORM). 2. En tiempo de ejecucin, como demuestra LISAG_DEClass.SCX se genera el mismo error "Objeto no contenido en el formulario". Puede seleccionar dos veces <Ignore> para que contine LISAG_DEClass.SCX 3. En tiempo de ejecucin, como demuestra LISAG_DEClass.SCX, aquellas mismas llamadas a mtodos de usuario de THISFORM que generaban un error cuando eran llamados desde el DataEnvironment.Init o desde el Init de uno de sus miembros, NO HACEN NADA si son llamados desde los eventos DataEnvironment.OpenTables o BeforeOpenTables ... los mtodos de usuario agregados al .SCX en la instancia actual son completamente ignorados. 4. En tiempo de ejecucin, como demuestra LISAG_DEClass2.SCX, cuando ocurren eventos como OpenTables and BeforeOpenTables del DataEnvironment, que se disparan despus del DataEnvironment.Init y el Init de sus miembros llaman a mtodos de THISFORM, slo se ejecuta el cdigo para aquellos mtodos que es heredado de la clase padre ... no el cdigo colocado en los mtodos del nivel actual instanciado. Conclusin La conclusin es que el cdigo colocado en mtodos de la instancia actual del formulario nunca se ejecuta cuando el mtodo es llamado desde eventos del DataEnvironment. Para confiar en los comportamientos abstractos del DataEnvironment, debe crear todo el cdigo en la clase DataEnvironment (jerrquicamente). Por ejemplo, algo que necesitamos a menudo es colocar SET TALK OFF antes de que se disparen los eventos del DataEnvironment, esto se debe hacer en DataEnvironment::Init y posiblemente tambin en el Init de su clase base Cursor. Para el nivel instanciado la configuracin de las propiedades no es fiable hasta que no se dispare el Form.Load. Dnde ubicar los comandos SET que estn limitados a la Sesin privada de datos? Varios comandos SET de VFP estn limitados a la Sesin privada de datos. Puede revisar la lista en el tpico SET DATASSESION del archivo Ayuda de VFP. Muchos de esos comandos SET afectan los datos y por tanto necesitan ser ejecutados para cada sesin privada de datos. Adems, SET TALK est limitado a la sesin privada de datos y debe ser establecido lo antes posible, en OFF que es el valor no -predeterminado para VFP, en el momento en que la sesin privada de datos (formulario) es instanciado, para eliminar las salidas TALK al _Screen o al formulario. Sin embargo, teniendo en cuenta las inconsistencias planteadas anteriormente en relacin con el objeto DataEnvironment, Cul es el mejor lugar para colocar los comandos SET en una sesin privada de datos, de forma tal que el formulario se instancia correctamente? Los archivos LISAG_PDS_SETS*.PRGs y sus correspondientes .SCX demuestran la solucin. Lo que nunca te dijo tu madre Eso depende de si utiliza o no DataEnvironment y en ese caso, si utiliza el DataEnvironment nativo de VFP o una clase personalizada DataEnvironment. No utiliza Dataenvironment Pienso que es la mejor eleccin, porque es ms consistente, y es ms fcil de mantener (vea la siguiente seccin de este documento). En ese caso, lo ms lgico es colocar los comandos SET de la sesin privada de datos en el Load del formulario de la ms alta jerarqua que establezca la propiedad DataSession a 2 -Private Data Session. Comience el Load de cada instancia de formulario con llamada hacia atrs (callback), para asegurarse que los comandos SET quedarn configurados desde el inicio: IF NOT DODEFAULT() RETURN .F. ENDIF DataEnvironment nativo en un formulario basado en .SCX En el formulario de la ms alta jerarqua establezca la propiedad DataSession a 2- Private Data Session. Agregue un mtodo de usuario para colocar la configuracin lgica de los comandos SET para su sesin privada de datos junto a cualquier otra cosa que necesite que ocurra al inicio del todo de la ejecucin del formulario. En el nivel instanciado, llame al mtodo de usuario desde los mtodos del DataEnvironment OpenTables o, mi preferido, BeforeOpenTables: THISFORM.CustomMethod()

http://www.portalfox.com/index.php?name=Sections&req=viewarticle&artid=42&page=1

15-06-2013

Lo que nunca te cont tu madre sobre instanciar y destruir formularios :: PortalFox :: Nada corre c...

Pgina 4 de 9

Recuerde, cualquier cdigo que coloque en el mtodo de usuario en el nivel instanciado no se ejecutar, como fue explicado en la seccin anterior ... solamente se ejecuta el cdigo colocado en la(s) clase(s) padre de la instancia actual. DataEnvironment de usuario con DEClass/DEClassLibrary Coloque la configuracin lgica de los comandos SET para su sesin privada de datos en el Init de la clase DataEnvironment. Sea consciente de que pudiera tener algunos comandos SET como SET TALK OFF en el Init de sus clases base Cursor o Relation, ya que estos se ejecutan antes que el Init de DataEnvironment. Cmo establecer los comandos SET? Una vez que haya determinado dnde necesita colocar su cdigo para los comandos SET de su sesin privada de datos, la cuestin es cmo debe escribir ese cdigo. Puede escribir un cdigo estricto con los comandos SET deseados (como se demuestra en LISAG_PDS_SETs.SCX); pero el mejor enfoque pudiera ser tener la sesin privada de datos que lea los valores desde la sesin predeterminada de datos ... frecuentemente se utiliza la misma configuracin de comandos SET, estos se configuran globalmente para la sesin predeterminada de datos cuando inicia su aplicacin (como se demuestra en LISAG_PDS_SETs_Abstract*.SCXs) Los ejemplos LISAG_PDS_SETs_Abstract*.PRGs y sus correspondientes .SCX demuestran una de estas tcnicas. Cada LISAG_PDS_SETs_Abstract*.PRG instancia un demo (objeto aplicacin) "application object", que contiene un mtodo de usuario GetSETCommandSetting(). Como el objeto application es instanciado en la sesin predeterminada de datos #1, al llamar a su mtodo GetSETCommandSetting() DEVUELVE la configuracin especificada como lo establece la sesin predeterminada de datos. Como la sesin privada de datos instancia, pueden establecer sus comandos SET para que coincidan con aquellos establecidos en la sesin predeterminada de datos llamado al objeto application global. Aqu est el cdigo esencial del mtodo SetSETCommands de la clase frmLISAG_PDS_SETs_Abstract en LISAG_PDS_SETs_Abstract.VCX: LOCAL laSETs[3], luSetting, lcString laSets[1] = "DELETED" laSets[2] = "MULTILOCKS" laSets[3] = "TALK" FOR EACH lcSet IN laSETs luSetting = goApplication.GetSetCommandSetting(m.lcSet) lcString = "SET " + m.lcSet + SPACE(1) + TRANSFORM(m.luSetting) &lcString ENDFOR Utilice DataEnvironment slo en tiempo de diseo: Antes de que decida abandonar del todo el uso del DataEnvironment, existe una idea que debe considerar: Mientras est diseando formularios basados en .SCX, utilice DataEnvironment solo para cuestiones de diseo: Arrastrar y soltar un(os) cursor(es) al formulario para crear instantneamente controles Grid. Arrastrar y soltar los campos al formulario para agregar controles cuyos ControlSource ya estarn definidos y con un ancho aproximado (Width) (si el mapeo de campos (field mapping) tiene establecido que incluya el ttulo del campo, se puede obtener ya la etiqueta correspondiente al Caption existente). Establecer el ControlSource de cualquier control desde la ventana propiedades, seleccionando uno los cursores actuales desde el cuadro desplegable. Tener acceso al DataEnvironment y sus miembros (cursores) en los generadores de usuarios. Recuerde establecer las propiedades AutoOpenTables y AutoCloseTables a .F. como muestra la Figura 5, entonces VFP ignora todo lo que est en el DataEnvironment en tiempo de ejecucin. Sin embargo, para formularios con sesin privada de datos, cuando el formulario es cerrado/destruido, VFP cierra todos los cursores abiertos mientras estuvo activo el formulario. En tiempo de ejecucin abra las vistas y tablas manualmente, utilizando una de las tcnicas descritas en el mtodo Load de DesignTimeDE.SCX, aprovechando sus ventajas sobre el comportamiento del DataEnvironment en tiempo de ejecucin: Si/cuando hay un problema al abrir una tabla/vista, se puede enviar un mensaje de usuario y devolver (RETURN) .F., interrumpir la instanciacin de esa tabla/vista mientras contina intacto el resto de la aplicacin. Por el contrario, cuando DataEnvironment encuentra un problema como un archivo no encontrado, cabecera de tabla daada, ndice daado, etc. El DataEnvironment falla, se interrumpe su ejecucin, as como todo el resto de la aplicacin. Se puede establecer el comando SET PATH antes de llamar los datos, de esta forma se puede intercambiar entre diferentes conjuntos de datos o simplemente ajustar la ruta (PATH) antes de llamar los datos Puede utilizar herramientas como Stonefield Database Toolkit para reparar problemas con tablas, ndices, memos, etc. Utilizando esta tcnica, puede ignorar las inconsistencias por utilizar DataEnvironment, que se han documentado anteriormente en este documento, ya que no hace nada en tiempo de ejecucin. Incluso en un diseo n-Capas, si sus objetos de Negocio proporcionan un cursor de datos puede agregar tablas/vistas al DataEnvironment (establecer propiedad Alias), all donde estn disponibles para conveniencia en tiempo de diseo y son ignoradas en tiempo de ejecucin, cuando el objeto negocio proporciona el dato real. Esta tcnica trabaja igualmente bien para formularios basados en .VCX ... no existe objeto DataEnvironment nativo, con lo cual tiene que cargar los datos e cdigo desde el mtodo Load y por tanto ignorar el DataEnvironment. Si se establecen las propiedades DEClass/DEClassLibrary, los datos indicados estn disponibles en tiempo de ejecucin pero el DataEnvironment no est disponible en tiempo de diseo. Mucho cuidado al romper la secuencia nativa de eventos de instanciacin Existen vas para romper la secuencia nativa de eventos de instanciacin. Algunas veces las consecuencias son menos graves, en otras son catastrficas y pueden causar todo tipo de comportamiento indeseado. Lo que tu madre nunca te dijo El ejemplo LISAG_SetFocus.SCX demuestra una de las posibilidades. Desafortunadamente esto es muy comn y muy fcil de hacer. La ltima lnea de cdigo en el evento Form.Init, es esta lnea aparentemente inocente que asegura que al instanciar, el botn OK tiene el foco: THIS.cmdOK.SetFocus() Sin embargo, cuando ejecute FORM LISAG_SetFocus, ver que el orden de los eventos no es LISAG, sino LIAGIS como se muestra en la figura 7. Cuando el Init realiza el SetFocus, VFP tiene que activar inmediatamente el formulario y darle el foco, para que el botn OK

http://www.portalfox.com/index.php?name=Sections&req=viewarticle&artid=42&page=1

15-06-2013

Lo que nunca te cont tu madre sobre instanciar y destruir formularios :: PortalFox :: Nada corre c...

Pgina 5 de 9

pueda tener el foco en ese punto. Despus de esa lnea de cdigo, el Form.Init ejecuta cualquier cdigo restante despus de esa lnea y contina con el evento Show. No muchos formularios dan el SetFocus al primer control de esta forma ... podra simplemente establecer el comando OK primero en el orden de tabulacin. Lo que es ms comn es establecer el foco condicionalmente a un control en particular, basado en alguna accin del formulario, basado a se vez, en un parmetro que reside en el Init. Simplemente he omitido esta condicin en el ejemplo LISAG_SetFocus.SCX para demostrar lo que ocurre cuando se encuentra la condicin. Pero, Cul es el dao? Eso depende de qu ha codificado en los otros eventos de instanciacin del formulario que normalmente se ejecutan en orden nativo una vez que el Init haya terminado completamente. Por ejemplo, puede tener cdigo en lo eventos Show y Activate que se disparan solamente si se est ejecutando durante la instanciacin del formulario (es posible hacerlo por programa - Show() de un formulario en cualquier momento y el Activate se ejecuta cada vez que el formulario se convierte en formulario activo, por ejemplo cuando el usuario hace clic en un formulario no modal abierto en el escritorio VFP) con este fin, agregue una propiedad de usuario (una bandera) con valor predeterminado a .T. , y lo hace igual a .F. en el Activate o GotFocus. El cdigo a travs del proceso de instanciacin pudiera ejecutar condicionalmente slo si el formulario est siendo instanciado: IF THIS.lInstantiating * realizar estas acciones solo si THISFORM se est instanciando ENDIF Ahora, si existe Member.SetFocus(), en el Form.Init la bandera se establece prematuramente en .F., antes de que el Init finalice y antes de que se ejecute el Show. Cualquier cdigo en Form.Show que se debe ejecutar solamente durante la instanciacin ser ignorado porque la bandera ya est en .F. en un escenario normal (no una demo) es un error muy difcil de depurar porque solo ocurre si la condicin encuentra el Member.SetFocus() explcito. El ejemplo demuestra como establecer ese tipo de propiedad de usuario Cul es la solucin? Entonces, qu se puede hacer en esos casos donde se necesita establecer condicionalmente el foco a un control particular al instanciar un formulario? El ejemplo LISAG_SetFocus1.SCX demuestra una tcnica. Adems de demostrar la idea de una propiedad lInstantiating, utilizada como bandera, aade un mtodo de usuario InitalSetFocus llamado desde Form.Activate slo durante la instanciacin. InitalSetFocus proporciona un lugar especfico para que el desarrollador ponga su cdigo para establecer el foco condicionalmente a un miembro en particular de un formulario ... sin romper la secuencia nativa de los eventos al instanciar. Cuando al llamar Form.Load() o Form.Init() devuelven .F. no se disparan los eventos Form.Destroy ni Form.Unload Devolviendo .F. desde el Load o el Init de un formulario no se instancia, es evidente; pero Lo que no te dijo tu madre 1. Como demuestra LISAG_QRDU_AbortLoad.SCX, cuando el Load devuelve .F., no se disparan ni Form.Destroy ni Form.Unload. 2. Como demuestra LISAG_QRDU_AbortInit.SCX, cuando el Init devuelve .F., se dispara Form.Unload; pero no lo hace Form.Destroy. Debido a que los miembros contenidos en el formulario se instancian antes que el Form.Init, el Destroy de esos miembros se dispara ya que estn fuera de lmites. (Since form members instantiate before the Form.Init, the Destroy of form members does fires as they go out of scope). Bueno, y qu? Bueno, pues frecuentemente colocamos el cdigo de limpieza en el Form.Destroy, y puede tener cdigo abstracto en el evento Destroy en la clase base o en otras clases Form que estn en la jerarqua del instanciado actualmente. Lo mismo se puede cumplir para Form.Unload. El cdigo de limpieza del Cleanup no se ejecuta cuando el Form.Load o el Form.Init devuelven .F. y el cdigo de limpieza del Cleanup no se ejecuta si el Form.Load devuelve .F.! Ejecutar Form.Destroy consistentemente Los formularios LISAG_QRDU_AbortLoadInit1.SCX and LISAG_QRDU_AbortLoadInit2.SCX demuestran tcnicas similares que puede utilizar para garantizar que los Form.Destroy/Unload (cdigos de limpieza) se ejecuten adecuadamente. Peculiaridad del SYS(1271) Primeramente vamos a mirar la funcin SYS(1271) que podemos poner a trabajar para nuestro beneficio. El ejemplo The LISAG_SYS1271.SCX demuestra que no puede hacer esta llamada: SYS(1271,THISFORM) Hasta que se haya completado Form.Load, no se puede llamar desde un mtodo de DataEnvionment, el Form.Load ni desde otro mtodo llamado desde el Form.Load. De hacerlo, recibir el mensaje, poco intuitivo, "Insuficiente memoria para completar esta operacin" (" Not enough memory to complete this operation"). Puede verlo al hacer DO FORM LISAG_SYS1271 ... seleccionar <Ignore> para continuar con la instalacin de LISAG_SYS1271.SCX. Por un lado, es inconveniente tener que esperar hasta el Form.Load para consultar SYS(1271,THISFORM) si desea realmente verificarlo antes. Pero LISAG_QRDU_AbortLoadInit1.SCX Utiliza este comportamiento para determinar cmo debe ser invocado Form.Destroy. LISAG_QRDU_AbortLoadInit1.SCX Los eventos Destroy e Init de LISAG_QRDU_AbortLoadInit1.SCX contienen una instruccin IF .F., tal que si la cambia por IF .T., provoca que el mtodo devuelva .F. La tcnica demostrada en LISAG_QRDU_AbortLoadInit1.SCX es: 1. Al devolver .F. desde Init/Load debido a un failed callback, NO incluya una llamada manual al Form.Destroy, asumiendo que cada clase padre Form que devuelva .F. desde su cdigo abstracto lo har. 2. Al devolver .F. desde Init/Load debido a cdigo en ese nivel, llama THIS.Destroy() antes del Return .F., asegurndose de que cualquier cdigo de limpieza en el Destroy se va a ejecutar adecuadamente. 3. El evento Form.Destroy contiene cdigo para llamar a SYS(1271,THISFORM) para determinar si el Destroy ocurre normalmente (no existe error en SYS(1271)) o debido a llamarlo explcitamente desde el Form.Load (SYS(1271) genera un error). Al llamar manualmente desde Form.Load, es llamado el Form.UnLoad. LISAG_QRDU_AbortLoadInit2.SCX

http://www.portalfox.com/index.php?name=Sections&req=viewarticle&artid=42&page=1

15-06-2013

Lo que nunca te cont tu madre sobre instanciar y destruir formularios :: PortalFox :: Nada corre c...

Pgina 6 de 9

Los eventos Destroy e Init de LISAG_QRDU_AbortLoadInit1.SCX contienen una instruccin IF .F., tal que si la cambia por IF .T., provoca que el mtodo devuelva .F. La tcnica demostrada en LISAG_QRDU_AbortLoadInit2.SCX es: 1. Al devolver .F. desde Init/Load debido a un failed callback, NO incluya una llamada manual al Form.Destroy, asumiendo que cada clase padre form que devuelva .F. desde su cdigo abstracto lo har. (igual que LISAG_QRDU_AbortLoadInit1.SCX) 2. Al devolver .F. desde Init/Load debido a cdigo en ese nivel, llama THIS.Destroy() antes del Return .F., asegurndose de que cualquier cdigo de limpieza en el Destroy se va a ejecutar adecuadamente. (igual que LISAG_QRDU_AbortLoadInit1.SCX) 3. El Form.Destroy contiene cdigo para verificar PROGRAM(PROGRAM(-1)-1) para determinar si el Destroy se ha llamado manualmente desde el Form.Load y, en tal caso, llama al Form.Unload. Referencia de objetos en la destruccin de formularios. (Object Reference Cleanup On Form Destruction) Para que cada objeto se libere / destruya adecuadamente, todas las referencias externas a sus miembros deben ser liberadas. Esto significa que para cerrar/destruir un formulario, cualquier objeto externo que mantenga referencia a uno o ms miembros debe liberarse explcitamente esa referencia o establecerse igual a .NULL. Si algunas de las referencias de objetos no se liberan explcitamente, el contenedor no se libera. Si el contenedor es un formulario o un contenedor dentro de un formulario, el formulario no se libera. Esta es la causa del error "Referencia de objeto daada" ("Dangling object reference"). ORCleanup1.PRG crea 2 instancias de ORCleanup1.SCX para demostrar el problema: 1. DO ORCleanup1 2. En la instancia 2 del formulario, seleccione cualquier casilla de verificacin del grupo inferior para guardar una referencia de objeto a un miembro de la instancia1 del formulario. 3. Intente cerrar la instancia 1: Haga clic en OK, haga clic en "X" a la derecha de la barra de ttulo, o seleccione Close desde el men en la caja de control de la barra de ttulo. La instancia 1 del formulario se niega a cerrarse, debido a referencias de objeto daadas, de hecho, si selecciona Cerrar desde el men en la caja de control de la barra de ttulo, la opcin Cerrar no aparece y la "X" a la derecha de la barra de ttulo aparece deshabilitada. 4. Cierre la instancia 2 del formulario. En cuanto se cierra, se cierra tambin la instancia1 ... su cdigo Destroy ya se haba disparado, solo estaba esperando a que se liberaran las referencias de objetos externos a sus miembros. Lo que no te dijo tu madre Para formularios, la limpieza de referencia de objetos nunca se debe hacer despus de Form.Destroy. Esto es fcil de hacer. Sin embargo, cuando los miembros del formulario necesitan limpiar las referencias de objetos, el cdigo colocado en su Destroy puede ser intil. Recuerde: el formulario destruye "de afuera hacia adentro", por tanto el Destroy de los miembros se dispara despus del Destroy del formulario como tal. Cuando se daa la referencia de objetos existente, se dispara el Form.Destroy; pero la destruccin frena aqu, y no se dispara ningn otro evento, incluyendo el Destroy de sus miembros, hasta que se libera la referencia de objeto daada. Esto es un gran problema cuando disea formularios de tal forma que un formulario no modal mantenga referencias de objetos a un miembro de otro formulario no modal u otros objetos externos. Dos casos comunes de dao de referencia de objetos: 1. Uno o ms miembros de Form1 contienen referencias de objeto a miembros de Form2. El usuario intenta cerrar Form2; pero se niega a cerrar hasta que los miembros de Form1 liberan sus referencias de objeto (este es el escenario que se muestra en ORCleanup1.PRG/.SCX). 2. Uno o ms miembros de Form1 contienen referencias de objeto a miembros de Form2. El usuario cierra Form1; pero Form1 no se cierra del todo, queda como un objeto artificial como una sesin de datos "desconocida" ("Unknown") visible en la ventana Sesin de datos. Cuando objetos externos contienen referencias a miembros de THISFORM ORCleanup1a.SCX demuestra una va para solucionar el comportamiento necesario. Repita los pasos para ORCleanup1 y observe la diferencia: 1. DO ORCleanup1a. 2. En la instancia2 del formulario, seleccione cualquier casilla de verificacin del grupo inferior para guardar una referencia de objeto a un miembro de la instancia1 del formulario. 3. Cierre la instancia 1del formulario. Se cierra normalmente, como se espera, an cuando aparentemente no se ha hecho la limpieza de referencia de objetos para liberar las referencias de la instancia 2 del formulario tiene a los objetos de la instancia 1 del formulario. Puede ver la tcnica que he utilizado en ORCleanup1a.SCX para examinar el cdigo en el evento Clic de cada casilla de verificacin del grupo inferior. He utilizado la funcin BINDEVENT() para asegurar que cuando se guarda una referencia de objeto, el Destroy de los objetos guardados del formulario llama automticamente al cdigo de limpieza de los objetos haciendo su almacenaje. Entonces, cuando es cerrado el formulario cuyas referencias a objetos miembros han sido guardadas, su Destroy llama al cdigo de limpieza de la referencia de objetos de los objetos externos manteniendo la referencia de objetos. He aqu lo que ocurre: 1. DO ORCleanup1a 2. Haga Clic en la primera casilla de verificacin del grupo inferior de la instancia 2 del formulario. El cdigo del evento Clic guarda una referencia de objeto al miembro txtDemo1 en la instancia 1 del formulario a la propiedad de usuario oFormMember de la instancia 2 del formulario. El cdigo Clic tambin ejecuta BINDEVENT() para asegurarse de que cuando se cierra la instancia 1 del formulario se ejecuta el mtodo ORCleanup de la instancia 2 del formulario. 3. Haga Clic en el botn de comandos OK de la instancia1 del formulario. Cuando se dispara el Destroy, ejecuta el mtodo Cleanup de la segunda instancia del formulario, gracias al BINDEVENT(). Entre otras cosas, el mtodo ORCleanup de la instancia 2 del formulario establece su propiedad oFormMember a .NULL., liberando la referencia de objeto guardada el txtDemo miembro de la instancia 1 del formulario. En un conjunto real de clases jerrquicas, pudiera probablemente agregar el mtodo ORCleanup a cada una de sus clases bases, de tal forma que el Checkbox.Click hara BINDEVENT() a su propio mtodo ORCleanup en lugar de THISFORM.ORCleanup, haciendo ms granular el control del proceso. Esta tcnica requiere, por supuesto, utilizar VFP 8.0, versin en la que fue agregada al lenguaje la poderosa funcin BINDEVENT(). Cuando los miembros de THISFORM contienen referencias a objetos externos Como se ha descrito antes, toda la limpieza a las referencias de objeto "garbage collection" debe hacerse antes de Form.Destroy, que ocurre antes que se dispare el evento Destroy de cualquier miembro.

http://www.portalfox.com/index.php?name=Sections&req=viewarticle&artid=42&page=1

15-06-2013

Lo que nunca te cont tu madre sobre instanciar y destruir formularios :: PortalFox :: Nada corre c...

Pgina 7 de 9

La solucin es codificar el Form.Destroy con mensajes a sus miembros para establecer explcitamente todas las referencias de objeto guardadas a .NULL. Mi sugerencia es separar esta tarea a un mtodo de usuario ORCleanup agregado a sus clases base. Primero, agregue un mtodo de usuario ORCleanup agregado a sus clases base Form. Agregue cdigo al Destroy de su clase base formulario para llamar a THISFORM.ORCleanup(). En cada clase formulario o instancia, cada vez que escriba cdigo para guardar una referencia de objeto a un miembro de formulario, asegrese de colocar el cdigo de liberacin correspondiente en el ORCleanup del formulario contenido. Cada vez que se dispara el Form.Destroy, es realizada la limpieza a todas las referencias de objeto. Sin embargo, puede que quiera abstraerse en lo delante de este comportamiento, ya que cuando est diseando la clase contenedora (pageframes, optiongroups, grids, containers, etc.), no puede colocar cdigo en el Destroy del formulario contenido porque no sabe qu instancia del formulario o clase va a contener finalmente el contenedor que est preparando. Para solucionar este problema, agregue un mtodo de usuario ORCleanup para cada una de sus clases base que pueden ser miembros de un formulario (Textbox, Spinner, Custom, etc.). Programe su clase base form Form.ORCleanup para interactuar con cada uno de sus miembros, llamando su mtodo ORCleanup. Si disea el mtodo ORCleanup de objetos contenedores (grids, pageframes, pages, optiongroups, containers, etc.) para interactuar con cada uno de sus miembros de la misma forma que el ORCleanup a nivel de formulario solo hace un lazo entre su arreglo de controles y llama al ORCleanup de cada uno de sus miembros directos. Daar la referencia de objetos cuando se utilizan colecciones Ver la serie de ejemplos ORCleanup2.SCX _Screen.ActiveForm y qu se puede hacer con esto Durante el curso de instanciacin del formulario hay un punto en el cual el formulario se convierte en _Screen.ActiveForm. Sabiendo cuando esto ocurre nos permite poder hacer cosas muy buenas con la referencia de objeto _Screen.ActiveForm. Cuando THISFORM se convierte en _Screen.ActiveForm Como se demuestra en el ejemplo _ScreenActiveForm.SCX, el formulario que se est instanciando se convierte en _Screen.ActiveForm inmediatamente despus de ser mostrados (Show()) Lo que nunca te dijo tu madre Desafortunadamente, _Screen.ActiveForm no es siempre _Screen.ActiveForm. En muchas ocasiones donde el formulario activo contiene uno o ms controles ActiveX, _Screen.ActiveForm puede ser una referencia de objeto a un control ActiveX, NO al formulario que lo contiene. La biblioteca en tiempo de ejecucin X6SAF.PRG para el framework Visual MaxFrame Profesional est incluida en esta sesin y controla este caso. Para tener una referencia de objeto confiable _Screen.ActiveForm, sustituya el cdigo por el siguiente: LOCAL loActiveForm loActiveForm = _Screen.ActiveForm IF TYPE("loActiveForm.BaseClass") = "C" * aqu no es el formulario activo ELSE * m.loActiveForm contiene una referencia de objeto fiable al formulario actualmente activo ENDIF lo que es el equivalente ms fiable: LOCAL loActiveForm loActiveForm = X6SAF() IF ISNULL(m.loActiveForm) * aqu no es el formulario activo ELSE * m.loActiveForm contiene una referencia de objeto fiable al formulario actualmente activo ENDIF Guardar una referencia al formulario y objeto llamados Al combinar los conocimientos sobre la secuencia de eventos de instanciacin de formulario LISAG y el conocimiento de cuando THISFORM se convierte en _Screen.ActiveForm, hay algunas cosas interesantes que puede hacer con esta informacin. El ejemplo _SAF1.SCX/_ScreenActiveForm1.SCX demuestra una buena tcnica para guardar fcilmente una referencia de objeto al formulario y objeto (si existe) llamados. Guardar una referencia al formulario llamado Cuando se ejecuta Form.Load, THISFORM no se ha instanciado todava, y no es el formulario activo _Screen.ActiveForm. Ms importante, cualquier formulario (si existe) que se ejecuta cuando THISFORM se llama se refleja aun en _Screen.ActiveForm. As, es como un pestaeo a obtener una referencia de objeto "libre" al formulario que se est ejecutando cuando se llama THISFORM, y lo guarda en una propiedad de usuario disponible durante la vida del THISFORM: LOCAL loActiveForm loActiveForm = X6SAF() IF VARTYPE(m.loActiveForm) = "O" THIS.oCallingForm = m.loActiveForm ELSE THIS.oCallingForm = .NULL. ENDIF A partir de ahora, THISFORM, puede "hablar" al formulario llamado via la referencia de objeto THISFORM.oCallingForm object. THISFORM.Destroy establece en .NULL. THISFORM.oCallingForm object: Algunas cosas interesantes sobre estas tcnicas: 1. Puede consultar informacin sobre el formulario llamado antes del THISFORM.Init, donde se reciben parmetros, aceptando otra configuracin que necesita optimizar lo que ocurre antes de que puedan ser verificados los parmetros. 2. No hay necesidad de llamar al formulario para pasar una referencia de objeto a s mismo al formulario llamado. 3. Cuando THISFORM se instancia desde cualquier sitio, como un men, no hay sitio para el formulario activo para pasar una referencia de objeto a si mismo a THISFORM.Init.

http://www.portalfox.com/index.php?name=Sections&req=viewarticle&artid=42&page=1

15-06-2013

Lo que nunca te cont tu madre sobre instanciar y destruir formularios :: PortalFox :: Nada corre c...

Pgina 8 de 9

4. Debido a que la referencia de objeto para el formulario llamado se guarda en una propiedad de usuario de THISFORM, est disponible durante la vida de THISFORM, luego de _Screen.ActiveForm se actualiza nunca despus de la referencia del formulario llamado. Estas caractersticas se muestran en el ejemplo _SAF1.SCX/_ScreenActiveForm1.SCX como se muestra en la figura 11: 1. El evento Clic del cmdCallForm en _SAF1.SCX un comando DO FORM ... no se pasan parmetros. 2. El evento Load del _ScreenActiveForm1.SCX contiene cdigo para verificar el formulario llamado, y entonces, guarda una referencia de objeto a su THISFORM.oCallingForm. 3. El evento Init del grdCustomers en _ScreenActiveForm1.SCX contiene cdigo para verificar el formulario llamado. En tal caso este formulario es consultado por su actual Orders.CustomerID, en cualquier caso, como se muestra en la figura 11. En ese caso grdCustomers establece el puntero a su registro inicial al CustomerID indicado. Observe que esta aplicacin tiene lugar antes que THISFORM.Init, donde un parmetro CustomerID pudiera ser recibido y aplicado. Puede DO FORM _ScreenActiveForm1.SCX con nada, y el cdigo descrito arriba simplemente encuentra que no hay mucho que puedas hacer. Abstraerlo! El comportamiento que ve en Load, Destroy, y ORCleanup puede abstraerse simplemente en su formulario base clase. Todos los formularios que heredarn esta caracterstica cada instancia lo utilicen o no. Guardar una referencia a un control del formulario llamado Al guardar una referencia de objeto de un formulario existente es fcil. Pero, qu tal si guardamos una referencia de objeto al control actual en el formulario; pero slo si este control est en la pila de ejecucin del programa, indicando que es responsable por llamar THISFORM (como el botn cmdCallForm en _SAF1.SCX)? Esto toma un poco ms de trabajo, puede fcilmente abstraerse de tal forma que est disponible para todos los formularios _ScreenActiveForm1.SCX contiene el cdigo necesario: 1. Load contiene un cdigo que verifica la pila de ejecucin del programa para la llamada del control en el formulario llamado, si se encuentra uno, se guarda una referencia en THISFORM.oCallingFormControl para utilizarlo mientras exista THISFORM. 2. Como se ha explicado previamente en este documento, en cualquier momento puede guardar una referencia de objeto para un miembro de objeto, debe proporcionar para la limpieza de la referencia de objeto. El Load de _ScreenActiveForm1.SCX hace que BINDEVENT() enlaza el Destroy del formulario llamado con THISFORM.ORCleanup. Si _ScreenActiveForm1.SCX es modal, esta accin no se requiere actualmente, porque THISFORM se puede cerrar antes de que el formulario puede ser llamado. 3. El Destroy de _ScreenActiveForm1.SCX llama a su ORCleanup de usuario. 4. El ORCleanup de_ScreenActiveForm1.SCX libera explcitamente las referencias de objeto oCallingForm y oCallingFormControl. Cuando THISFORM es no modal, se dispara el Destroy del formulario llamado THISFORM.ORCleanup gracias al BINDEVENT() en THISFORM.Load Observe que la referencia de objeto del formulario llamado, si este control es realmente llamado por el formulario llamado, se guarda slo en THISFORM.oCallingFormControl. Por ejemplo, si se ejecuta un formulario cuando se llama un segundo formulario desde otro lado como una opcin de men, el control activo en el formulario activo no llama al segundo formulario. THISFORM.oCallingFormControl no se guarda debido a que un mtodo del control activo en el formulario activo no est en la pila de ejecucin del programa. Entonces, como se ha explicado en el comentario del cdigo al final del mtodo Load de _ScreenActiveForm1.SCX, puede decirlo fcilmente si THISFORM.oCallingForm es llamado por THISFORM, o fue simplemente el formulario llamado cuando es llamado THISFORM. Estas caractersticas se demuestran en el ejemplo _SAF1.SCX/_ScreenActiveForm1.SCX, como muestra la Figura 12: 1. El Init de _ScreenActiveForm1.SCX determina sus posiciones Top y Left relativos al control llamado, si existe. Las referencias de objeto a un formulario llamado y su control llamado podra pasar al Init, en lugar de utilizar la referencia de objeto THIS.oCallingFormControl guardada en Load. Sin embargo, esto requiere que el desarrollador que codifica la llamada al formulario recordando que pase siempre los parmetros necesarios a _ScreenActiveForm1.SCX, y en el orden correcto. 2. El AfterRowColChange de grdCustomers en _ScreenActiveForm1.SCX actualiza el Caption del botn del formulario llamado. Esto es solamente por propsitos de diversin / demo, para mostrar cual fcil es "hablar" al control del formulario llamado. 3. El ORCleanup de _ScreenActiveForm1.SCX contiene cdigo para cambiar el Caption del botn del formulario llamado. Puede adems hacer DO FORM _ScreenActiveForm1 con nada, y el cdigo escrito antes encuentra que no hay mucho que hacer. Abstraerlo! El comportamiento que ve en el Load, Destroy, y ORCleanup se puede abstraer fcilmente en su clase base formulario. Todos los formularios heredan esta caracterstica aunque la utilice o no la instancia indicada. Mantener referencias de objetos a los formularios llamados El ejemplo _SAF1.SCX/_ScreenActiveForm1.SCX descrito en la seccin previa pudiera hacerle pensar sobre una tcnica ms poderosa. En ese caso, seguramente disfrutar de esta. Me han preguntado por un cdigo para situaciones donde el formulario no modal necesite no slo llamar a uno o ms formularios no modales adicionales; pero adems para cada uno de los formularios llamados para mantener las referencias de objeto a cada otro as que puede actualizar cada otro de tiempo en tiempo. Con ms frecuencia me han preguntado algunos desarrolladores que han tratado de implementar una situacin, como para depurarlo. No es trivial, mantener todas las referencias de objeto en ambas direcciones, ni para asegurarse que esa limpieza de referencia de objeto (garbage collection) fue hecha para prevenir una posibilidad de daar la referencia de objeto. Gracias a la funcin BINDEVENT() agregada en VFP 8.0, este escenario es ahora muy fcil de implementar. Mejor an, el cdigo necesario es fcil de conceptuar. La serie de ejemplos FormORCleanupCaller*.SCX contienen el cdigo. Todos los formularios en la serie FormORCleanupCaller*.SCX estn basados en el ejemplo de la clase base formulario frmBaseForm en la biblioteca FormORCleanup.VCX. La tcnica es manipular todo sin pasar ningn parmetro, utilizando una tcnica similar demostrada en el ejemplo _SAF1.SCX/_ScreenActiveForm1.SCX. Aqu vemos los conceptos en frmBaseForm: 1. La llamada desde Load llama al mtodo de usuario StoreCalledForm. 2. La llamada desde StoreCalledForm verifica si existe un formulario llamado. En ese caso:<ol type="a"> 3. La referencia de objeto a un formulario llamado desde cualquier objeto llamado es guardada en THISFORM.oCallingForm y THISFORM.oCallingFormControl.

http://www.portalfox.com/index.php?name=Sections&req=viewarticle&artid=42&page=1

15-06-2013

Lo que nunca te cont tu madre sobre instanciar y destruir formularios :: PortalFox :: Nada corre c...

Pgina 9 de 9

4. Un BINDEVENT() es utilizado para asegurar que cuando se cierra el formulario llamado, es llamadoTHISFORM.ORCleanupCallingForm, donde las referencias de objeto se guardan en THISFORM.oCallingForm y son liberados THISFORM.oCallingFormControl, permitiendo que el formulario llamado cierre adecuadamente. 5. Si el formulario llamado tiene un mtodo de usuario StoreCalledForm (ser si hereda desde frmBaseForm), es llamado su mtodo StoreCalledForm y se pasa una referencia de objeto a THISFORM. La llamada desde StoreCalledForm:<ol type="a"> Guarda una referencia de objeto al formulario llamado en una propiedad de arreglo (Tuve problemas de hacer una propiedad coleccin para trabajar adecuadamente). Utilice un BINDEVENT() para asegurar que cuando se cierra el formulario llamado (el que est actualmente bajo instanciacin), el mtodo ORCleanupCalledForm del formulario llamado es llamado, donde es liberada la referencia de objeto al formulario llamado. El resultado en cadena es que ambas llamadas y llamados formularios mantengan la referencia de objetos entre ellos. Cada formulario llamado puede tener una referencia de objeto a slo un formulario invocador; pero cada formulario invocador puede mantener la referencia a un nmero ilimitado de formularios invocados. Ms importante, es manipulada la necesaria limpieza de referencia de objeto. Cada instancia de formulario hereda todo el comportamiento necesario, y el formulario o sus miembros puede simplemente THISFORM., THISFORM.oCallingFormObject, y THISFORM.aCalledForms[] objetos en cualquier momento (por supuesto, despus de verificar para ver si son referencias vlidas de objeto) Este comportamiento se demuestra de esta forma, como se observa en las figuras 14 y 15: 1. DO FormORCleanupCaller1.SCX 2. Haga Clic en cualquiera de los botones DO FORM. Si hace Clic en alguno de ellos ms de una vez, se cargan nuevas instancias de FormORCleanupCalled*.SCX sobre las ya existentes. 3. Haga Clic en el botn <?> de cualquier formulario para ver en cada formulario sobre el otro invocador/invocado. 4. Haga Clic en el botn <OK> de cualquier formulario para que vea que no hay problemas de referencias de objetos daadas. FormORCleanupCaller2.SCX El ejemplo FormORCleanupCaller2.SCX es el mismo que FormORCleanupCaller1.SCX, excepto que cuando el formulario invocador es cerrado, todos los formulario que llama se cierran automticamente. Encontrar el cdigo para esto en el evento Destroy. Nota: El autor ha dado su autorizacin, y los ejemplos se pueden descargar de: DrewSpeedieDemo.zip (167 KB).

[ Volver a Drew Speedie | ndice de secciones ]

Todas las marcas y los logos utilizados en este sitio son propiedad de sus respectivos dueos. Los artculos, noticias y comentarios son propiedad y responsabilidad de sus respectivos autores. Copyright 2000-2013 PortalFox. Todos los derechos reservados.

http://www.portalfox.com/index.php?name=Sections&req=viewarticle&artid=42&page=1

15-06-2013

Anda mungkin juga menyukai