2 de diciembre de 2017

Extraer el oro del XSource - Parte 1

Artículo original: Mining for Gold in XSource
https://msdn.microsoft.com/en-us/library/ms947609.aspx
Autor: Doug Hennig
Traducido por: Ana María Bisbé York


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.

Descubriendo Xsource

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 XSourceObjetivoLocalización
AddLabelAddLabel applicationTools\AddLabel\AddLabel.APP
BrowserClass Browser y Component GalleryBrowser.APP y Gallery.APP
BuildersMain 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\BuildersArchivos comunes utilizados en generadores -
Builders\CmdBldrCommand Group BuilderWizards\CmdBldr.APP
Builders\EditBldrEditBox BuilderWizards\EditBldr.APP
Builders\FormBldrForm BuilderWizards\FormBldr.APP
Builders\GridBldrGrid BuilderWizards\GridBldr.APP
Builders\ListBldrListBox BuilderWizards\ListBldr.APP
Builders\RIBuildrReferential Integrity BuilderWizards\RIBuildr.APP
Builders\StylBldrStyle BuilderWizards\StylBldr.APP
ConvertConverter (convierte archivos a versiones nuevas)Convert.APP
CoverageCoverage ProfilerCoverage.APP
DataExplorer (VFP9)Data Explorer (disponible en Task Pane Manager)DataExplorer.APP
EnvMgrEnvironment Manager (disponible en Task Pane Manager)EnvMgr.APP
FoxCodeIntelliSense ManagerFoxCode.APP
FoxRefCode ReferencesFoxRef.APP
OBrowserObject BrowserObjectBrowser.APP
ReportBuilder (VFP9)Diálogos y otros comportamientos para el Diseñador de informesReportBuilder.APP
ReportOutput (VFP9)Report listeners (for example, XML y HTML)ReportOutput.APP
ReportPreview (VFP9)New report preview dialogReportPreview.APP
TaskListTask ListTaskList.APP
TaskPaneTask Pane ManagerTaskPane.APP
ToolBoxToolBoxToolBox.APP
VFPCleanUtilidad para restaurar archivos de usuarios de VFP y parámetros de registroVFPClean.APP
WebServiceWeb Services PublisherWizards\WebService.APP
Wizards\AutomateArchivos comunes utilizados en los asistentes Graph, Mail Merge, y PivotTable -
Wizards\DEBuilderCursorAdapter y DataEnvironment BuildersWizards\DEBuilder.APP
Wizards\WZAppApplication WizardWizards\WZApp.APP
Wizards\WZCommonArchivos comunes (Common) utilizados en asistentes y generadores-
Wizards\WZFormForm WizardWizards\WZForm.APP
Wizards\WZFoxDocDocumenting WizardWizards\WZFoxDoc.APP
Wizards\WZGraphGraph WizardWizards\WZGraph.APP
Wizards\WZImportImport WizardWizards\WZImport.APP
Wizards\WZIntNetWWW Search Page WizardNo longer included as tool
Wizards\WZMailMail Merge WizardWizards\WZMail.APP
Wizards\WZPivotPivotTable WizardWizards\WZPivot.APP
Wizards\WZQueryQuery WizardWizards\WZQuery.APP
Wizards\WZReportReport y Label WizardsWizards\WZReport.APP
Wizards\WZTableTable y Database WizardsWizards\WZTable.APP y
Wizards\WZDBC.APP
Wizards\WZUpsizeUpsizing WizardWizards\WZUpsize.APP
Wizards\WZWebWeb Publishing WizardWizards\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.

Ventajas del código XSource

¿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)
endwith

Hablando 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
ENDIF 

El 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')
endwith

Para 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)

Conclusiones

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.

Download

Download FT04_10Hennig.zip

No hay comentarios. :

Publicar un comentario

Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.