Extraer el oro del XSource
23/03/2020
Autor: Doug Hennig
Introducción
Visual FoxPro viene con código fuente para la mayoría de las herramientas Xbase que salen con el producto, incluyendo Class Browser (Examinador de clases), Code References (Referencias de código), Toolbox (Caja de herramientas), y Task Pane (Panel de tareas). El observar el código fuente trae como consecuencia nuevas y poderosas herramientas escritas por los gurús más importantes de VFP. En este artículo, el primero de la serie, Doug Hennig comenta sobre varios archivos en el XSource para mostrar muy buenas ideas y código que se puede utilizar en aplicaciones, por ejemplo: guardar la configuración de usuarios en archivos de recursos y crear menús contextuales orientados a objetos.
Comenzando con la edición de Diciembre 1998, escribí una serie de tres partes en FoxTalk llamada "Mining for Gold in the FFC" que debatía algunos aspectos de la utilidad del código fuente en la carpeta FFC (FoxPro Foundation Classes) hija de la carpeta raiz de VFP. Sin embargo, ¡ VFP viene con mucho más código fuente que eso !
Muchas otras herramientas que vienen con VFP fueron en realidad escritas en VFP en lugar de C++ (lenguaje utilizado para la creación de VFP). Se les conoce como herramientas "Xbase" para distinguirlas de componentes internos. Algunas de las herramientas Xbase están disponibles desde el menú Tools (Herramientas), tales como: Class Browser (Examinador de clases), IntelliSense Manager (Administrador de IntelliSense), Toolbox (Caja de herramientas), Task Pane Manager (Administrador del Panel de tareas), y Report Wizard (Asistente de informes). Otras están disponibles en el contexto de sus sitios apropiados, tales como el Generador de Integridad Referencial, Generador de CursorAdapter, y en VFP 9.0, ReportBuilder.APP, que proporciona los diálogos nuevos para el Diseñador de informes.
Este artículo es el primero en una serie que explora XSource, buscando técnicas interesantes y código fuente que podemos utilizar en nuestras aplicaciones. No voy a explorar todo el código Xsource en esta serie, que tomaría todo un libro. En su lugar, voy a comentar sobre ciertas cosas que son útiles, incluyendo:
- Cómo trabajan las barras de herramientas estilo Outlook.
- Cómo utilizar el archivo de recursos FOXUSER como una alternativa a archivos INI o el Registro de Windows.
- Mostrar archivos AVI en formularios VFP.
- Crear menús contextuales orientados a objetos.
Por varias razones, ahora VFP ha salido con el código fuente de casi todas las herramientas Xbase. (Beautify.APP, que proporciona la funcionalidad para el elemento de menú Beautify (Presentación), es una excepción). Debido a que es mucho código, viene todo dentro de un archivo ZIP: XSource.ZIP en la carpeta Tools\XSource debajo de la carpeta raíz de VFP. Al descompactar esos archivos resulta la carpeta hija de Tools\XSource llamada VFPSource, que contiene el código fuente para las herramientas XBase. La tabla 1 lista el objetivo de cada carpeta en XSource y la localización de cada archivo APP generado en el código.
Tabla1. Objetivo y localización de las aplicaciones creadas con código fuente XSource.
Carpeta XSource Objetivo Localización AddLabel AddLabel application Tools\AddLabel\AddLabel.APP Browser Class Browser y Component Gallery Browser.APP y Gallery.APP Builders Main builder program (delega en su correspondiente generador de APP) y main wizard program (delega en el correspondiente asistente de APP) Builder.APP y Wizard.APP Builders\Builders Archivos comunes utilizados en generadores - Builders\CmdBldr Command Group Builder Wizards\CmdBldr.APP Builders\EditBldr EditBox Builder Wizards\EditBldr.APP Builders\FormBldr Form Builder Wizards\FormBldr.APP Builders\GridBldr Grid Builder Wizards\GridBldr.APP Builders\ListBldr ListBox Builder Wizards\ListBldr.APP Builders\RIBuildr Referential Integrity Builder Wizards\RIBuildr.APP Builders\StylBldr Style Builder Wizards\StylBldr.APP Convert Converter (convierte archivos a versiones nuevas) Convert.APP Coverage Coverage Profiler Coverage.APP DataExplorer (VFP9) Data Explorer (disponible en Task Pane Manager) DataExplorer.APP EnvMgr Environment Manager (disponible en Task Pane Manager) EnvMgr.APP FoxCode IntelliSense Manager FoxCode.APP FoxRef Code References FoxRef.APP OBrowser Object Browser ObjectBrowser.APP ReportBuilder (VFP9) Diálogos y otros comportamientos para el Diseñador de informes ReportBuilder.APP ReportOutput (VFP9) Report listeners (for example, XML y HTML) ReportOutput.APP ReportPreview (VFP9) New report preview dialog ReportPreview.APP TaskList Task List TaskList.APP TaskPane Task Pane Manager TaskPane.APP ToolBox ToolBox ToolBox.APP VFPClean Utilidad para restaurar archivos de usuarios de VFP y parámetros de registro VFPClean.APP WebService Web Services Publisher Wizards\WebService.APP Wizards\Automate Archivos comunes utilizados en los asistentes Graph, Mail Merge, y PivotTable - Wizards\DEBuilder CursorAdapter y DataEnvironment Builders Wizards\DEBuilder.APP Wizards\WZApp Application Wizard Wizards\WZApp.APP Wizards\WZCommon Archivos comunes (Common) utilizados en asistentes y generadores - Wizards\WZForm Form Wizard Wizards\WZForm.APP Wizards\WZFoxDoc Documenting Wizard Wizards\WZFoxDoc.APP Wizards\WZGraph Graph Wizard Wizards\WZGraph.APP Wizards\WZImport Import Wizard Wizards\WZImport.APP Wizards\WZIntNet WWW Search Page Wizard No longer included as tool Wizards\WZMail Mail Merge Wizard Wizards\WZMail.APP Wizards\WZPivot PivotTable Wizard Wizards\WZPivot.APP Wizards\WZQuery Query Wizard Wizards\WZQuery.APP Wizards\WZReport Report y Label Wizards Wizards\WZReport.APP Wizards\WZTable Table y Database Wizards Wizards\WZTable.APP y
Wizards\WZDBC.APPWizards\WZUpsize Upsizing Wizard Wizards\WZUpsize.APP Wizards\WZWeb Web Publishing Wizard Wizards\WZWeb.APP Nota: Algunas otras carpetas de Tools–Analyzer, CPZero, Filer, GenDBC, HexEdit, MSAA, y Test contienen código fuente para las carpetas en estos directorios. Estas herramientas no son accesibles desde los menús de VFP, son herramientas StandAlone disponibles al ejecutarlas en el archivo apropiado en la carpeta apropiada. No voy a comentar sobre estas herramientas en esta serie.
¿Qué tiene de bueno tener el código fuente para estas herramientas? Al menos dos cosas:
- Si una de estas herramientas no hace exactamente lo que deseas, puedes cambiarlo y generar un nuevo archivo APP.
- Yo siempre gusto de resaltar el trabajo de otros, y las herramientas XBase VFP fueron escritas por algunas de las mejores y más brillantes estrellas del universo VFP. (Muchas de ellas fueron escritas por no-empleados de MS, como Markus Egger, que escribió el Examinador de objetos.) Se han utilizado técnicas muy interesantes en algunas de estas herramientas, y he aprendido mucho explorando el código fuente. Lo más importante, muchas de las clases, imágenes y PRGs son útiles para otras aplicaciones.
Si decide utilizar algo del código fuente XSource en sus propias aplicaciones, sea consciente de que algunas clases y subclases están en diferentes VCX, y algunos de los VCX tienen gran cantidad de clases en ellos. Incluso, si necesita solamente una de ellas, todas las clases relacionadas, PRGs e imágenes se van a incorporar a su proyecto cuando lo genere.
Puede incluso no tener esto en cuenta (el único efecto negativo es el incremento del tamaño del EXE y el espacio del disco es barato en nuestros días) o colocar las clases que desea en un VCX propio. Herramientas como Class Browser y White Light Computing's HackCX (una herramienta anteriormente de Geeks y Gurus sobre la que escribí en mi artículo de Diciembre 2003, "Tools for and by Geeks and Gurus," y altamente recomendada ver:www.whitelightcomputing.com) puede ayudar en esto, aunque puede ser un poco complicado de dominar.
Nota: El archivo de Ayuda de VFP (Help File) es un poco confuso al tratar el tema de reutilizar código desde XSource; pero Microsoft recientemente indicó que es posible utilizar el código de XSource ya que este código ha sido levemente modificado y podemos utilizar incluso bibliotecas de clases enteras si fueron incluidas en el proyecto y en la aplicación generada. Esto es similar a las reglas para utilizar código fuente y bibliotecas de la carpeta Sanples, que se describe en el tema "Características de archivos distribuibles y restringidos de Visual FoxPro"
La riqueza de la fuente de imágenes
Cuando estaba buscando imágenes para los botones o iconos para formularios, uno de los primeros lugares donde fui a buscar fue el Xsource, especialmente la carpeta de las herramientas Xbase más novedosas (aquellas que fueron añadidas en VFP 8 o superior). Con frecuencia encuentro exactamente lo que ando buscando, o a veces, algo que necesito sólo retocar con el Paint. La figura 1 muestra algunas de las imágenes útiles que he encontrado en la carpeta XSource.
Figura 1
Guardar la configuración del archivo de recursos
La escritura de aplicaciones profesionalmente guarda cierta configuración, de tal forma que la siguiente vez que el usuario lo ejecute, utilice nuevamente la misma configuración. Por ejemplo, es bueno, que una aplicación "recuerde" el tamaño y posición de los formularios, la ubicación desde dónde se importó el último archivo importado, qué tipo de archivo exportado creado la última vez, y así sucesivamente. Hay varios lugares de la aplicación que pueden almacenar estos parámetros, incluyendo archivos INI, el Registro Windows, un archivo XML o una tabla.
El entorno de desarrollo de VFP (IDE) hace un gran trabajo al recordar cosas como el tamaño, posición, y texto seleccionado en los archivos de código y la última ventana abierta en un formulario o clase. Esta configuración se guarda en su archivo de recursos, típicamente FoxUser.DBF en su carpeta HOME(7). ¿No sería magnífico poder utilizar este archivo de recursos para guardar su propia configuración?
Esto es exactamente lo que hace la clase FoxResource en FoxResource.PRG. Esta clase, que se encuentra en varias carpetas incluyendo Toolbox, proporciona métodos para leer de y escribir en un archivo de recursos. Debe ser el archivo actual (esto es, uno devuelto por SYS(2005)); puede especificar el nombre utilizado en la propiedad ResourceFile.
Un archivo de recursos tiene los siguientes campos:
- TYPE - contiene el tipo de registro. LA mayoría de los registros tienen "PREFW" en este campo; pero puede utilizar cualquier valor que desee para configurar la propiedad ResourceType de FoxResource (el valor predeterminado es "PREFW".
- ID - El ID del registro. Puede ser cualquier valor que desee; pero debe ser único.
- NAME - Nombre para el registro. Puede quedar en blanco si lo desea.
- READONLY - .T. Si el registro no se puede cambiar.
- CKVAL - El checksum (suma de verificación) para el campo DATA.
- DATA - Los parámetros. FoxResource guarda sus parámetros como una matriz utilizando SAVE TO MEMO DATA.
- UPDATED - La fecha de la última actualización del registro.
La clase FoxResource utiliza el método Load para cargar un conjunto de parámetros desde un registro en el archivo de recursos. Pasa al método el ID y el nombre (en correspondencia con los campos ID y NAME en el archivo) y cargará la configuración (en caso de que exista ese registro) en una colección interna de elementos nombre-valor. Una vez que haya cargado el conjunto, puede recuperar los valores pasando al método Get el nombre del parámetro que desea recuperar.
No se tiene que preocupar sobre los tipos de datos, una vez que los parámetros son guardados como una matriz en el archivo de recursos, los valores regresan con el mismo tipo de datos con el que fueron guardados. Hay un cambio interesante en archivos INI, donde todo es una cadena. Si el parámetro que necesita no existe, el método Get devuelve .NULL., entonces probablemente utilizará la función NVL() en el valor devuelto.
He aquí un ejemplo que mantiene lo que viene en Top si no existe este parámetro.
This.Top = nvl(oFoxResource.Get('Top'), This.Top)Puede verificar si existe un parámetro o no utilizando el método OptionExists. Si le pasa el nombre del parámetro y devuelve .T., entonces existe el parámetro.
Para guardar un parámetro, llame al método Set, pásele el nombre y valor a configurar. Una vez que ha guardado un conjunto de parámetros, puede guardar un conjunto entero al archivo de recursos con el método Save. Este método espera los mismos valores para los parámetros ID y nombre que el método Load. Save además, actualiza las columnas UPDATED y CKVAL y admite registros de sólo lectura en el archivo de recursos rehusando actualizar el registro si el campo READONLY es .T.
Existen otros métodos, menos útiles. Clear limpia la colección, GetData devuelve el contenido del campo memo DATA para el registro especificado, SaveTo guarda en un campo memo especificado en lugar de en el campo memo DATA y RestoreFrom restaura desde un campo memo especificado.
TestMenu.SCX, que se incluye en el archivo de descarga de este mes, demuestra el uso de FoxResource. El método Init instancia FoxResource, carga los parámetros para el nombre TESTMENU, llama a SetFormPosition para restaurar el tamaño y posición del formulario, y entonces, utiliza el método Get de FoxResource para restaurar el puntero de registro y filtro.
local lnRecno, ; lcFilter with This .oResourceOptions = newobject('FoxResource', ; home() + 'Tools\XSource\VFPSource\Toolbox\' + ; 'FoxResource.prg') .oResourceOptions.Load('TESTMENU') .SetFormPosition() lnRecno = nvl(.oResourceOptions.Get('RecNo'), 0) if between(lnRecno, 1, reccount()) go lnRecno endif between(lnRecno, 1, reccount()) lcFilter = nvl(.oResourceOptions.Get('Filter'), '') .SetFilter(lcFilter) endwithHablando de "chupar descaradamente" - ups!, quiero decir valorar código de otros, el código para el método SetFormPosition viene directamente del método de igual nombre en la clase cFoxForm de la biblioteca ToolboxCtrls.VCX en la carpeta de código fuente ToolBox. Este código hace mucho más que simplemente recuperar las propiedades del formulario Top, Left, Width, y Height, además se asegura de que estos valores no provoquen que el formulario quede fuera de la pantalla, en caso de que haya sido abierto a la derecha y con una resolución muy alta y luego haya sido abierto con una resolución más baja:
LOCAL nTop, nLeft, nWidth, nHeight IF !ISNULL(THIS.oResourceOptions) m.nHeight = THIS.oResourceOptions.Get("HEIGHT") IF VARTYPE(m.nHeight) == 'N' AND m.nHeight >= 0 THIS.Height = m.nHeight ENDIF m.nWidth = THIS.oResourceOptions.Get("WIDTH") IF VARTYPE(m.nWidth) == 'N' AND m.nWidth >= 0 THIS.Width = m.nWidth ENDIF m.nTop = THIS.oResourceOptions.Get("TOP") IF VARTYPE(m.nTop) == 'N' * asegúrese de que está visible IF !THIS.Desktop IF m.nTop > _SCREEN.Height m.nTop = MAX(_SCREEN.Height - THIS.Height, 0) ELSE IF m.nTop + THIS.Height < 0 m.nTop = 0 ENDIF ENDIF ENDIF THIS.Top = m.nTop ENDIF m.nLeft = THIS.oResourceOptions.Get("LEFT") IF VARTYPE(m.nLeft) == 'N' * asegúrese de que está visible IF !THIS.Desktop IF m.nLeft > _SCREEN.Width m.nLeft = MAX(_SCREEN.Width - THIS.Width, 0) ELSE IF m.nLeft + THIS.Width < 0 m.nLeft = 0 ENDIF ENDIF ENDIF THIS.Left = m.nLeft ENDIF ENDIFEl método Destroy guarda la configuración que deseamos preservar y luego escribe todo eso en el conjunto TESTMENU en el archivo de recursos.
with This .oResourceOptions.Set('Left', .Left) .oResourceOptions.Set('Top', .Top) .oResourceOptions.Set('Height', .Height) .oResourceOptions.Set('Width', .Width) .oResourceOptions.Set('Filter', .cFilterName) .oResourceOptions.Set('RecNo', recno()) .oResourceOptions.Save('TESTMENU') endwithPara probarlo, ejecute TestMenu.SCX y mueva el formulario para otro lugar de la pantalla. Haga Clic derecho sobre el formulario y seleccione una configuración de filtro desde el submenú setfilter y luego haga clic derecho y seleccione Next en varis ocasiones. (Luego vemos cómo fue creado el menú contextual) Cierre este formulario y luego re-ejecútelo; se debe abrir la misma posición con el mismo filtro y el mismo registro que tenía al cerrarlo.
Menús contextuales orientados a objetos
Los menús de sistema de VFP es uno de los pocos aspectos del producto que se mantienen procedural en lugar de basados en objetos. Aunque puede crear sus menús propios utilizando los comandos relativos al menú (tales como DEFINE POPUP y DEFINE BAR), casi nadie lo hace porque VFP incluye el Diseñador de Menú, que permite que cree menús visualmente. Sin embargo, el mayor problema en la utilización de menús generados por el Diseñador de Menú es que ellos no pueden ser alterados en tiempo de ejecución. Esto significa que no puede realizar búsquedas fácilmente de ellos o mostrar u ocultar barras específicas bajo ciertas condiciones.
¡ ContextMenu llega al rescate ! Esta clase, definida en FoxMenu.PRG en la carpeta de código fuente ToolBox, permite crear un menú contextual al vuelo, sin tener que escribir los tan feos comandos DEFINE POPUP y DEFINE BAR. Simplemente instancie la clase, establezca las barras según su deseo (el que podría ser utilizar código condicional para localizar el menú o decidir qué barras incluir), y dígale que muestre el menú. Podría incluso manipular el menú con datos si desea.
Para agregar barras al menú, llame al método AddMenu. Este método acepta hasta seis parámetros (los últimos cuatro son opcionales): el prompt para la barra, una expresión para ejecutarla cuando se selecciona la barra, el nombre de un archivo imagen a utilizar por la imagen de la barra, .T. si está la marca , .T., si la barra está activa y .T. si la barra aparece en negrita.
Debido a que el menú de sistema de VFP vive fuera de su objeto de sistema, las referencias del tipo This o ThisForm, no van a trabajar en la expresión a ejecutar, entonces, coloque una referencia al formulario u objeto en una variable privada y utilice esa variable en la expresión. El código de ejemplo del archivo Download ilustra esto.
AddMenu devuelve una referencia a un objeto que contiene propiedades sobre la barra de menú: Caption, Picture, Checked, ActionCode, IsEnabled, y Bold. Es más interesante, sin embargo, que contiene además una propiedad SubMenu que contiene una referencia a otro objeto ContextMenu. Entonces, para crear un submenú para una barra, simplemente llame al método AddMenu del objeto SubMenu.
Para mostrar el menú, llama al método Show. En la versión VFP 8, puede opcionalmente pasar dos parámetros: la fila y la columna en la que se debe mostrar el menú. La versión 9 añade un tercer parámetro, opcional: el nombre del formulario en el cual debe aparecer el menú (ContextMenu utiliza en la cláusula IN WINDOW del comando DEFINE POPUP en este caso). Si llama a Show sin parámetros, el menú no será colocado a la derecha. En su lugar, pasa cualquiera MROW('') y MCOL('') para la fila y columna y omite el nombre del formulario, u omite la fila y columna y pasa This.Name para el nombre del formulario.
Un bug en el método BuildMenu de la beta pública de VFP 9 de esta clase impide que trabaje del todo bien. Falta un punto y coma luego de "m.nCol" en el siguiente código. Asegúrese de agregarla en su copia de FoxMenu.PRG. (Nota: Debe ser corregido en la versión definitiva de VFP 9.)
DEFINE POPUP (m.cMenuName) SHORTCUT ; RELATIVE FROM m.nRow, m.nCol ; IN WINDOW (m.cFormName)ContextMenu tiene un par de propiedades. MenuBarCount contiene la cantidad de barras en el menú. ShowInScreen se supone que indique que ele menú sea mostrado en _SCREEN, pero todo el código que referencia esta propiedad se comenta fuera, así que podemos ignorarla.
He aquí un ejemplo, tomado del método ShowMenu de TestMenu.SCX (vea Figura 2). Instancia la clase ContextMenu que llama al método AddMenu para crear varias barras para mostrar. El código que crea las barras de navegación (First, Next, Previous, y Last) pasa la imagen y admite parámetros para que las barras tengan imágenes y están habilitados sólo cuando corresponde. La barra Set Filter tiene un submenú de diferentes tipos de filtros que se pueden establecer, y la barra que se corresponde con el filtro actual tiene su marca activada.
Figura 2
local loMenu, ; loMenuItem private poForm * Crea el objeto menu. loMenu = newobject('ContextMenu', ; home() + ; 'Tools\XSource\VFPSource\Toolbox\FoxMenu.prg') * Crea una referencia privada a este formulario * para quetrabajen las acciones del menú. poForm = This * Define las barras de menú. loMenu.AddMenu('First', 'poForm.FirstRecord()', ; 'frsrec_s.bmp', .F., not This.lFirstRecord) loMenu.AddMenu('Next', 'poForm.NextRecord()', ; 'nxtrec_s.bmp', .F., not This.lLastRecord) loMenu.AddMenu('Previous', 'poForm.PreviousRecord()', ; 'prvrec_s.bmp', .F., not This.lFirstRecord) loMenu.AddMenu('Last', 'poForm.LastRecord()', ; 'lstrec_s.bmp', .F., not This.lLastRecord) loMenu.AddMenu('\-') loMenuItem = loMenu.AddMenu('Set Filter') loMenuItem.SubMenu.AddMenu('North American Customers', ; 'poForm.SetFilter("NA")', '', This.cFilterName = 'NA') loMenuItem.SubMenu.AddMenu('European Customers', ; 'poForm.SetFilter("EU")', '', This.cFilterName = 'EU') loMenuItem.SubMenu.AddMenu('South American Customers', ; 'poForm.SetFilter("SA")', '', This.cFilterName = 'SA') loMenuItem.SubMenu.AddMenu('\-') loMenuItem.SubMenu.AddMenu('Clear Filter', ; 'poForm.SetFilter("")', '', empty(This.cFilterName)) loMenu.AddMenu('\-') loMenu.AddMenu('Close', 'poForm.Release()') * Muestra el menú. loMenu.Show(, , This.Name)
Xsource no es solamente código fuente para las herramientas Xbase que vienen con VFP; es además una rica fuente de técnicas y código reutilizable. El próximo mes, voy a comentar sobre técnicas y código que muestran archivos de videos en formularios, mostrando una barra de progreso durante un proceso prolongado y agregando una barra de Outlook a sus aplicaciones.
2ª parte
En la primera parte de esta serie, Doug Hennig examinó algunos componentes interesantes en XSource, el código fuente de la mayoría de herramientas "Xbase" que vienen con VFP. El artículo de este mes continúa escarbando por el código que usted puede utilizar en sus propias aplicaciones.
Como he descrito en el artículo del mes pasado, el código fuente para casi todas las herramientas XBase viene con VFP en un archivo .ZIP: XSource.ZIP en la carpeta Tools\XSource debajo de la carpeta raíz de VFP. Al descomprimir este archivo obtenemos como resultado una carpeta llamada VFPSource que queda debajo de Tools\XSource que contiene el código fuente para las herramientas XBase. En la Parte 1, he comentado sobre algunas de las imágenes que se incluyen en el XSource y componentes que puede utilizar para guardar la configuración de los recursos de VFP y para crear menús contextuales orientados a objetos. Este mes, voy a mostrar otros componentes que puede utilizar en sus propias aplicaciones.
Cuando elimina o mueve un grupo de archivos, Explorador de Windows despliega un diálogo muy agradable que muestra una versión animada de qué es lo que se está haciendo. Esto no hace que el proceso sea más rápido; pero al menos permite conocer que el sistema no se ha quedado parado mientras procesa la acción.
Hace varias versiones, VFP salió con archivos animados en la carpeta Graphics\Videos. Puede utilizar estos videos para representar el mismo tipo de diálogo de proceso que tiene Explorador de Windows. La clase cAnimation en FoxRef.VCX (en la carpeta FoxRef XSource) hace esta labor de forma sencilla. Arrastre simplemente la clase a un formulario y llame a su método Load, pasando el nombre del archivo de video a mostrar. Si desea cargar el archivo; pero no ejecutarlo inmediatamente, establezca lAutoPlay = .F., llame a Load para que cargue el archivo y luego llame a Play cuando esté listo para ejecutar el video. La clase cAnimation es una subclase de Microsoft Animation Control, así que es necesario distribuir este control ActiveX (MSCOMCT2.OCX) con su aplicación.
Para hacerlo aun más fácil de usar, he creado una subclase de cAnimation llamada SFAnimation en SFXSource.VCX. La subclase tiene una propiedad cFileName que contiene el nombre del archivo de video, y su método Init carga al Load, pasándole This.cFileName. Entonces, simplemente arrastro el control SFAnimation en un formulario, establezco cFileName y ya está listo.
Para ver un ejemplo de este control, ejecute TestAnimation.SCX, incluido en el archivo Download de este mes. Ejecuta seis archivos de video que vienen con VFP, mostrando animaciones similares a las que ve en el diálogo del Explorador de Windows. La figura 1 muestra la apariencia de este formulario durante la ejecución.
Figura 1
Mostrar un video animado no siempre brinda al usuario suficiente información sobre el progreso de un proceso de larga duración. Muchas veces, los usuarios desean verificar el progreso de procesos de múltiples pasos o desean ver un termómetro que indique cuán largo ha sido un proceso. La clase cProgressForm de FoxToolbox.VCX en la carpeta ToolBox XSource brinda una barra de progreso muy agradable que puede utilizar para una tarea de este tipo ( Existe también una versión de esta clase en FoxRef.VCX en la carpeta FoxRef XSource; pero la versión ToolBox tiene un poco más de posibilidades.)
Como puede ver en la figura 2, cProgressForm tiene varias posibilidades:
- Una imagen animada - el PC con la lente de aumento - que indica que algo está ocurriendo (no es un archivo de video, es un GIF animado).
- Una descripción general del proceso.
- Un termómetro que indica el progreso del proceso.
- Un mensaje de estado que muestra qué paso del proceso se está ejecutando actualmente.
- Un botón cancelar, que permite al usuario terminar el proceso.
Figura 2
Cuando instancia cProgressForm, puede pasarle opcionalmente la descripción del proceso (algo como "Recuperando datos") para establecer el valor de Caption de la etiqueta Description. Puede además, establecer la descripción llamando al método SetDescription. En la medida que va progresando el proceso, puede querer actualizar el Caption de la etiqueta Status debajo del termómetro para indicar en qué paso se encuentra, pase la cadena deseada al método SetStatus.
Si desea que el termómetro muestre valores absolutos, tales como registro actual de todos los registros existentes, en lugar del porcentaje de ejecución, establezca el valor máximo para el termómetro llamando a SetMax. Si no quiere que se muestre el botón Cancel, establezca la propiedad lShowCancel a .F.. Si no desea que se muestre el termómetro, establezca la propiedad lShowThermometer a .F.
Para actualizar el termómetro, llame al método SetProgress, pase el valor al termómetro (ya sea como un valor absoluto si llama a SetMax o como un valor porcentual, por ejemplo, 50 cuando el proceso está cumplido a la mitad) y una cadena opcional para utilizar como el Caption para la etiqueta Status, SetProgress devuelve .T. si el proceso debe continuar o .F. si se debe detener, debido a que el usuario ha presionado el botón cancelar.
cProgressForm tiene un par de comportamientos que yo deseaba modificar. Primero, debemos manualmente llamar al método Show para mostrar el formulario. Yo preferiría que lo mostrara automáticamente si es necesario la primera vez que es llamado SetProgress. Segundo, establece una propiedad de usuario nSecond a SECONDS() en el Init. Esta propiedad puede utilizarse para determinar el tiempo total requerido para un proceso; pero, estableciéndolo en el Init puede ser muy pronto. Entonces, he creado una subclase llamada SFProgressForm en SFXSource.VCX. El método Init utiliza DODEFAULT() y establece entonces nSeconds a 0. El método SetProgress simplemente hace Visible = .T. si el formulario no es visible y nSeconds a SECONDS() si fue 0, y utiliza luego DODEFAULT() para el comportamiento habitual.
Uno de los archivos que se incluyen en el archivo Download, TestProgress.PRG, es un ejemplo de que busca los archivos en la carpeta FFC en la carpeta Home de VFP para localizar cadenas (constantes con "_LOC" en el nombre). Esto utiliza SetMax para especificar la cantidad total para procesar, y actualizar el medidor de progreso después de que cada archivo ha sido buscado. Quite los comentarios al código estableciendo lShowCancel o lShowThermometer a .F. para ver que cómo se ve la barra de progreso en cada caso.
* Determinar cuántos ficheros deseamos procesar. lnFiles = adir(laFiles, home() + 'ffc\*.*') * Instanciar el diálogo de progreso y dar valor a algunas propiedades. loProgress = newobject('cProgressForm', 'SFXSource.vcx') loProgress.SetDescription('Buscando las cadenas localizadas ') loProgress.SetMax(lnFiles) *loProgress.lShowCancel = .F. *loProgress.lShowThermometer = .F. * Buscar los archivos según las cadenas localizadas, * actualizando el medidor de progreso según vamos. lnFound = 0 for lnI = 1 to lnFiles lcFile = laFiles[lnI, 1] lcFullFile = forcepath(lcFile, home() + 'ffc') lcExt = justext(lcFile) do case case inlist(lcExt, 'H', 'PRG') lcContents = filetostr(lcFullFile) lnFound = lnFound + occurs('_LOC', ; upper(lcContents)) case lcExt = 'VCX' use (lcFullFile) again shared lnFound = lnFound + occurs('_LOC', upper(METHODS)) endcase if not loProgress.SetProgress(lnI, 'Verificando ' + ; lcFile + '...') exit endif not loProgress.SetProgress ... next lnI * Mostrar los resultados messagebox(transform(lnFound) + 'instancias encontradas ' + ; 'en ' + transform(seconds() - loProgress.nSeconds) + ; ' segundos.')Vea que cProgressForm utiliza el control ActiveX Microsoft ProgressBar, entonces necesitará distribuir MSCOMCT.OCX con su aplicación. Además, utiliza un archivo GIF animado, FindComp.GIF en la carpeta BitMaps de la carpeta Toolbox XSource, entonces este archivo sería agregado a su proyecto cuando lo genera.
Aun cuando ha sido liberado hace muchos años, Outlook ha influido en la apariencia y el comportamiento de otras aplicaciones. Una de las interfaces qué más ha sido copiada ha sido la de Outlook Estos controles tienen un conjunto de categorías plegables, cada una de las cuales contienen elementos individuales que pueden seleccionar que determina qué aparece a la derecha del Outlook.
Existen controles ActiveX que se pueden adquirir que simulan la apariencia de la barra Outlook, algunos de los controles trabajan con VFP y muchos otros no lo hacen. Sin embargo, existe una aplicación que viene con VFP que utiliza un control similar, y tenemos el código fuente en el XSource: el Toolbox.
El Toolbox se agregó en VFP 8 como una vía para propiciar una interfaz similar a Visual Studio.NET para seleccionar controles y otros elementos frecuentemente utilizados. Debido a que no tiene apariencia exacta a Outlook, yo llamaría al control que mantiene las categoría y elementos debajo de cada uno del "toolbox".
Como puede ver en la Figura 3, la caja de herramientas (toolbox) consta de cuatro áreas de interés (sin contar el texto de ayuda al final). Una categoría representa un conjunto de elementos. Las categorías son como las barras en el toolbox. Solamente una categoría se expande en cada momento, el resto aparece como barra de categoría. Al hacer Clic en una barra, esta se expande, lo que provoca que la que estaba actualmente expandida se contrae.
Figura 3
Una herramienta es un elemento dentro de una categoría. Las herramientas tienen una imagen, un nombre, y un ToolTip. Puede realizar varias acciones en la herramienta: hacer clic, hacer doble clic, arrastrar, etc. Además, dentro de una categoría hay botones de desplazamiento hacia arriba y abajo. No necesita hacer clic sobre estos botones, simplemente desplace el puntero del ratón sobre un botón por un segundo y comenzará a desplazar la lista en la dirección indicada.
Las clases que proporcionan el toolbox están en FoxToolbox.VCX y _Toolbox.VCX en la carpeta Toolbox XSource. La clase de interés en FoxToolbox.VCX es ToolContainer. Contiene los controles y la lógica para una simple categoría. Un formulario que muestra una caja de herramientas va a contener tantos objetos ToolContainer como categorías haya que mostrar.
La propiedad oToolItem de cada ToolContainer tiene una referencia a un objeto _Category ( o uno de sus subclases, las diferentes subclases son utilizadas por tipos diferentes de categorías en la aplicación VFP Toolbox) desde _Toolbox.VCX, _Category tiene propiedades y métodos para la categoría. Para nuestro uso, _Category no es realmente importante; pero algunos métodos de ToolContainer requieren de que exista.
Cada ToolContainer también mantiene una colección de herramientas, cada una de las cuales es representada por un objeto _Tool (o una de sus subclases, subclases diferentes son utilizadas para diferentes tipos de herramientas en la aplicación Toolbox de VFP) desde _Toolbox.VCX. _Tool tiene propiedades y métodos para una herramienta.
El formulario principal en la aplicación Toolbox se basa en la clase ToolboxForm en FoxToolbox.VCX. De inicio consideré subclasear este formulario; pero desafortunadamente es demasiado dependiente de las tareas relacionadas con la aplicación Toolbox en lugar de un toolbox genérico. Entonces, en su lugar, he creado SFToolboxForm (en SFXSource.VCX) como la base para formularios con una barra de herramientas.
El método ShowToolbox, llamado desde el Init, es el responsable de crear el toolbox.
local loCategories, ; lnI, ; loCategory, ; lcCategory, ; loContainer, ; loTools, ; lnJ, ; loTool with This * Tomar una colección de categorías y procesar cada una. loCategories = .GetCategories() .nCategories = loCategories.Count for lnI = 1 to .nCategories loCategory = loCategories.Item(lnI) * Crear un objeto ToolContainer y establecer sus propiedades. lcCategory = 'Category' + loCategory.cID .NewObject(lcCategory, 'ToolContainer', ; 'FoxToolbox.vcx') loContainer = evaluate('.' + lcCategory) with loContainer .SetFont(This.FontName, This.FontSize, '') .oToolItem = newobject('_Category', ; '_Toolbox.vcx') .CategoryID = loCategory.cID .Caption = loCategory.cName .Width = This.nToolboxWidth .Visible = .T. endwith * Si esta es la primera categoría, fija la altura estándar * para las categorías según la altura de esta y hace que sea * la abierta de forma predeterminada if lnI = 1 .nStandardHeight = loContainer.Height .cCurrentCategory = loCategory.cID endif lnI = 1 * Toma una colección de herramientas para la categoría actual. * Para cada una, enlaza su método OnClick con nuestro método * ToolSelected. La clase toolbox espera su propiedad * oEngine referencie un objeto con una propiedad * DblClickToOpen, entonces, crea una. Agrega el objeto herramienta * al contenedor de categorías. loTools = .GetToolsForCategory(loCategory.cID) for lnJ = 1 to loTools.Count loTool = loTools.Item(lnJ) bindevent(loTool, 'OnClick', This, 'ToolSelected') loTool.oEngine = createobject('Empty') addproperty(loTool.oEngine, 'DblClickToOpen', .F.) loContainer.AddTool(loTool) next lnJ next lnI * Ajusta los tamaños de las categorías. .ResizeToolbox() endwithEste código comienza llamando al método GetCategories para devolver una colección de objetos con información sobre cada categoría. GetCategories es abstracto en esta clase porque el mecanismo exacto para llenar la colección puede variar. El código entonces puede ir a través de la colección, agregando un objeto ToolContainer al formulario de cada categoría. El ancho del ToolContainer se establece en la propiedad nToolboxWidth del formulario, para asegurarse de establecer esta propiedad en una subclase o formulario creado desde esta clase.
Solamente para la primera categoría, el código establece la propiedad nStandardHeight, que contiene el estándar (esto es, cerrado) altura para cada categoría, para la altura del contenedor actual. Además establece cCurrentCategory al ID para esta categoría de tal forma que se abrirá automáticamente en el código que explicaré más tarde.
Luego, el código llama al método GetToolsForCategory, el que es abstracto en esta clase, para devolver una colección de objetos_Tool que representan las herramientas para la categoría especificada. Cuando un usuario hace clic en una herramienta, el toolbox llama al método OnClick en el objeto herramienta apropiado. Decidí controlar los clics en el nivel formulario, entonces, el código utiliza BINDEVENT() para enlazar el método OnClick de cada objeto herramienta del método ToolSelected del formulario. Debido a que varios métodos toolbox esperan que la propiedad oEngine de un objeto herramienta referencie un objeto con una propiedad DblClickToOpen, el código crea un objeto. El objeto herramienta es agregado a la colección de herramientas en la categoría contenedor.
Finalmente, ShowToolbox llama al método Resize Toolbox para abrir la categoría seleccionada (en este caso, la primera) y cierra las otras. Este código no se muestra aquí por razones de espacio, examínelo con toda libertad.
El método SetCategory es llamado desde el código toolbox cuando el usuario hace clic sobre una categoría. Todo este código establece cCurrentCategory al ID para la categoría seleccionada y llama a ResizeToolbox, para controlar la nueva selección.
SFToolboxForm contiene otros tres métodos abstractos: OnRightClick, llamado cuando el usuario hace clic derecho sobre una herramienta, ResetHelpFile, la que establece el archivo Help a uno predeterminado, y SetHelpText, el que, en la aplicación Toolbox, además alguna información acerca de la herramienta seleccionada dentro del área Help al final del formulario. Estos métodos son necesarios debido a que algunos métodos del toolbox lo llaman. Aunque ellos no hacen nada en esta clase, puede implementar el comportamiento apropiado en una subclase.
TestToolbox.SCX es un formulario de ejemplo basados en SFToolboxForm. Esto utiliza una caja de herramientas para mostrar los diferentes módulos en una aplicación contable. Lea tres módulos desde una tabla MODULES, el que contiene registros para las categorías (tales como "Cuentas admisibles" y "Entrada de órdenes") y el módulo debajo de cada uno (como "Usuarios" y "Ordenes") Tiene el siguiente código en GetCategories, el método llamado por ShowToolbox para llenar una colección de categorías.
local loCollection, ; loCategory loCollection = createobject('Collection') select NAME, ID ; from MODULES ; where PARENTID = 0 and ACTIVE ; into cursor _CATEGORIES scan loCategory = createobject('Empty') addproperty(loCategory, 'cName', trim(NAME)) addproperty(loCategory, 'cID', transform(ID)) loCollection.Add(loCategory) endscan use return loCollectionEste código llena una colección con objetos que tienen propiedades cName y cID desde las categorías en la tabla MODULES (aquellos registros con ParentID = 0).
GetToolsForCategory llamado además desde ShowToolbox, es responsable para llenar una colección de herramientas para la categoría especificada.
lparameters tcID local loCollection, ; loCategory loCollection = createobject('Collection') select NAME, ID, CLASS, LIBRARY, IMAGEFILE ; from MODULES ; where PARENTID = val(tcID) and ACTIVE ; into cursor _MODULES scan loModule = newobject('_Tool', '_Toolbox.vcx') with loModule .ToolName = trim(NAME) .UniqueID = transform(ID) .ParentID = tcID .ImageFile = textmerge(trim(IMAGEFILE)) addproperty(loModule, 'cClass', trim(CLASS)) addproperty(loModule, 'cLibrary', trim(LIBRARY)) endwith loCollection.Add(loModule) endscan use return loCollectionEste código selecciona los registros desde Modules que tienen ParentID conteniendo el ID de la categoría para llenar. Cada objeto en la colección es un objeto _Tool, con sus propiedades establecidas adecuadamente. Este código agrega además un par de propiedades de usuario: la clase y la librería para el contenedor de los controles a mostrar a la derecha del formulario cuando la herramienta es seleccionada.
Gracias a BINDEVENT(), el método ToolSelected es llamado cuando el usuario hace clic sobre la herramienta. ToolSelected agrega un objeto a la derecha del formulario empleando las propiedades cClass y cLibrary de la herramienta seleccionada para indicar que clase utiliza el objeto.
local llLockScreen, ; laEvents[1], ; loModule, ; lcClass, ; lcLibrary, ; llHaveIt, ; lnI, ; loControl, ; llDesiredModule with This * Bloqueamos la pantalla y no vemos los cambios * hasta que no hemos acabado. llLockScreen = .LockScreen .LockScreen = .T. * Tomamos la clase y la biblioteca que suponemos vamos a utilizar * desde el módulo seleccionado. aevents(laEvents, 0) loModule = laEvents[1] lcClass = lower(loModule.cClass) lcLibrary = lower(loModule.cLibrary) * Ocultar todos los módulos, menos el seleccionado. llHaveIt = .F. for lnI = 1 to .ControlCount loControl = .Controls[lnI] if pemstatus(loControl, 'cAlias', 5) llDesiredModule = ; lower(loControl.Class) == lcClass llHaveIt = llHaveIt or llDesiredModule loControl.Visible = llDesiredModule endif pemstatus(loControl, 'cAlias', 5) ... next lnI * Instanciar la clase adecuada si lo necesitamos. if not llHaveIt try .NewObject(lcClass, lcClass, lcLibrary) loControl = evaluate('.' + lcClass) with loControl .Left = 210 .Width = This.Width - .Left .Height = This.Height if version(5) >= 900 .Anchor = 15 else .OnResize() endif version(5) >= 900 .Visible = .T. .SetupControls() endwith catch to loException messagebox('Este módulo no se ha definido.') endtry endif not llHaveIt * Refrescar la pantalla. .LockScreen = llLockScreen endwithLa Figura 4 muestra que este formulario de la misma forma que se ejecuta.
Figura 4
Mostrar archivos de video en formularios de VFP, mostrar un medidor de progreso durante un proceso largo y agregar una barra tipo Outlook a sus aplicaciones son tareas que se simplifican si se utilizan los componentes que vienen con VFP en XSource. En mi próximo artículo veremos como crear una zona con desplazamiento en un formulario y cuán fácil es crear generadores utilizando componentes XSource.
Samples - Link alternativo
Mine for Code in XSource - Artículo original en inglés
Extraer el oro del XSource - Este documento
Artículos originales:
- Mine for Code in XSource - Parte 1 - https://msdn.microsoft.com/en-us/library/ms947609.aspx
- Mine for Code in XSource - Parte 2 - http://msdn.microsoft.com/library/en-us/dnfoxtk04/html/ft04k8.asp
- Mine for Code in XSource - Parte 3 - http://msdn.microsoft.com/library/en-us/dnfoxtk04/html/ft04l6.asp
Autor: Doug Hennig
Traducido por: Ana María Bisbé York