8 de diciembre de 2016

VFP9 - Mejoras relativas al Entorno de desarrollo de Visual FoxPro

Toda la interfaz de desarrollo incorporada por VFP está orientada al desarrollo rápido de aplicaciones RAD (Rapid Application Development). La versión 9.0 incorpora cambios y mejoras en muchos de los componentes del IDE de VFP. Me referiré sólo a aquellos que me parecen más relevantes.

Diseñador de clases y formularios

En esta versión hay cambios, por citar algunos:

  • En opciones de menú y títulos de ventanas.
  • El valor inicial a la nueva propiedad se puede introducir directamente. En las imágenes se muestra la diferencia entre el IDE en VFP 8.0 y VFP 9.0
  • Se pueden acoplar formularios, sobre esto recordemos que la posibilidad de acoplar ventanas del sistema comenzó en la versión 7.0, la versión 8.0 aportó el control por programación y ahora la 9.0 permite acoplar ventanas de formularios.

Ventana propiedades

El área donde VFP 9.0 tiene más cambios relativos al Diseño de formularios y clases es sin dudas la ventana Propiedades que incluye cambios como:

  • Iconos que preceden las propiedades, eventos, y métodos (PEM)
  • Posibilidad de una Fuente personalizada, para ello basta con hacer Clic derecho en el panel superior de la ventana Propiedades y seleccionar Font. Aparece el cuadro de diálogo Fuente que nos permite configurar la fuente deseada.
  • Personalizar colores para identificar distintos tipos de PEMs (activex, no predeterminados, de usuario y los instanciados). Para ello es necesario hacer Clic derecho sobre la parte superior de la ventana Propiedades, y seleccionar la opción a la que se desea cambiar el color (ActiveX / Non-default Properties / Custom Properties / Intance Properties Color)

    Estas figuras muestran la diferencia entre las dos últimas versiones.

  • Las propiedades admiten cadenas de más de 255 caracteres
  • La ventana Zoom admite introducir caracteres extendidos en la cadena de propiedades, tales como retornos y avance de línea. Esta ventana está disponible para propiedades cuyos valores puedan ser textos largos. Si hace Clic derecho sobre una propiedad con estas condiciones se muestra en su menú la opción Zoom.
  • Nueva ficha Favoritos es de gran utilidad ya que ahorramos tiempo, teniendo solo los PEMs utilizadas más frecuentemente. Esta ficha no cumple aun con todos los deseos de los desarrolladores. Se han solicitado dos mejoras:
    • Una casilla de verificación desde la ventana de Nueva Propiedad / Nuevo método que permita enviar al nuevo elemento, directamente a la ficha Favoritos.
    • Una opción en el menú contextual que permita eliminar un elemento de la ficha Favoritos, de igual forma que existe una opción que lo agrega.

Esta versión por fin pudo satisfacer dos peticiones muy antiguas de los desarrolladores VFP: La preservación de la capitalización en PEMs de usuario y la ficha Favoritos. Esto se ha logrado a través de una nueva propiedad: _MemberData.

_MemberData - Es una cadena XML que contiene el dato VFP, entre las etiquetas <VFPData> y </VFPData> sólo hay un elemento que se llama memberdata para cada PEM que tenga. A su vez cada elemento tiene un atributo por cada aspecto que se haya configurado para cada PEM.

Los atributos son:

  • Type: Tipo del miembro contenido. Valores posibles "property", "event" o "method"
  • Name: Nombre del miembro contenido Ejemplo: enabled / caption / mipropiedad
  • Display: Forma en que aparece el nombre tanto en la ventana propiedades como en IntelliSense Ej Enabled / Caption / MiPropiedad.
  • Favorities "True": aparece en la ficha Favoritos, en caso contrario, "False"
  • Override (Sobreescribir) "True": ignora el metadato de la clase padre, "False" - hereda el metadato de la clase padre.
  • Script: Código que se ejecuta cuando se selecciona el miembro contenido en la ventana Propiedades.

Veamos un ejemplo:

<VFPData>
<memberdata name="fontbold" type="Property" favorites="True"/>
<memberdata name="caption" type="property" override="True"/>
<memberdata name="comment" type="property" script="INPUTBOX('Comentarios')"/>
<memberdata name="nuevapropiedad" type="property" display="NuevaPropiedad"/>
</VFPData> 

En este ejemplo vemos lo siguiente:

  • Hay 4 propiedades contenidas en este dato: fontbold, caption, nuevapropiedad y comment
  • La propiedad fontbold está en la ficha Favoritos
  • La propiedad caption se puede sobrescribir, no hereda del metadato de su clase padre.
  • La propiedad comment aparece en la ficha Favoritos y tiene un código script asociado, en este caso invoca una función InputBox.
  • La propiedad nueva propiedad se mostrará como NuevaPropiedad
  • Las propiedades fontbold, caption y comment se muestran como FontBold, Caption y Comment, aunque no quede explícitamente expresado en el texto del metadato y esto se debe a que son propiedades nativas de VFP.

Podemos resumir las características y funcionalidades de _MemberData con lo siguiente:

  • MemberData controla la ficha Favoritos
  • La propiedad _MemberData no viene de forma predeterminada, hay que agregarla a los controles. Esto es fácil de hacer agregándola a cada una de las clases base.
  • Está inicialmente vacía, al actualizarla, en una cadena XML, quedan especificadas las propiedades a configurar para ambos tipos de PEMs (nativos o de usuario). El motor MemberData sirve de esta forma, para extender la ficha propiedades, la cual lee el XML y aplica las personalizaciones indicadas.
  • MemberData controla la capitalización de las palabras
  • Está disponible para PEMs nativas y de usuarios
  • Se aplica al formulario o la clase como tal, no a los objetos contenidos. Si el objeto contenido ya tiene una propiedad _memberdata se puede editar. En caso contrario no.
  • Para agregar un PEM a la ficha Favoritos. Con el cursor en el elemento que se desee agregar, hacer Clic derecho y seleccionar la opción del menú Add to Favorities (Agregar a favoritos)
  • Para quitar un elemento de la ficha Favoritos no es tan sencillo como un acceso directo, hay que modificar la cadena XML o cambiarlo en el Editor de Memberdata
  • No admite & para macro sustitución, en su lugar hay que indicar &amp;
  • El generador de MemberData permite la creación automática de la cadena XML
  • VFP 9.0 incluye una utilidad MemberDataEditor.APP que simplifica grandemente todo el trabajo con el del motor MemberData.
  • MemberData es una ventana abierta a la creación de editores de propiedades personalizados. vía la sección Script del XML de _MemberData. Una vez que ha especificado un script para una propiedad de usuario, VFP muestra el botón editor de propiedades a la derecha del cuadro de texto del valor de la propiedad. Este es el mismo botón que muestra VFP para las propiedades nativas, por ejemplo ForeColor. El equipo VFP incorporó un script a la función Caption invocando un InputBox. Aprovechando esta posibilidad se incorporó un editor para la propiedad Anchor que también es Novedad en VFP 9.0 y veremos a continuación.

Ejemplo: el Editor MemberData para la propiedad Comment, a la que se ha agregado con Script.

Resultado en la ventana Propiedades, mostrando el botón editor de propiedades a la derecha del cuadro de texto del valor de la propiedad Comment.

Propiedad Anchor

Para determinar si el control contenido debe ser redimensionado y/o movido en dependencia a los cambios ocurridos en su contenedor y /o a que ejes debe anclarse.

Esta propiedad incluye un editor, para facilitar el cálculo del valor a asignar a esta propiedad. Veamos el aspecto que tiene este editor.

Para trabajar con el editor sólo será necesario hacer Clic en las barras que separan al control del contenedor estableciendo de esta forma el valor numérico que tomará la propiedad. Esta propiedad tiene gran importancia, porque permite lograr de forma nativa una funcionalidad que los desarrolladores estábamos obligados a incorporar por programación.

Mostrar elementos gráficos

Los elementos gráficos: formas y líneas existen desde VFP 3.0; pero siempre ha sido muy limitada su funcionalidad. VFP 90 introdujo varios cambios, entre ellos:

  • Rotar etiquetas – Nueva propiedad Rotation que se aplica a Label, Line y Shape que permite rotar el objeto según los grados que se indiquen. En el caso de Line y Shape sólo es válida si fueron creados con la propiedad PolyPoints.
  • Crear y rotar formas complejas - Los objetos Line y Shape contienen ahora la propiedad PolyPoints que se puede utilizar para especificar una matriz de puntos que determina la línea o forma mostrada. Esta propiedad permite manejar matrices unidimensionales y bidimensionales, en este caso, los elementos impares representan las x y los elementos pares las y.

Auto Completar en cuadros de texto

Esta técnica sigue el rastro de las entradas del usuario en un campo determinado. Es una buena funcionalidad a ofrecer al usuario final que hasta ahora no existía de forma nativa en VFP.

Se ha implementado a través de nuevas propiedades: AutoCompSource, AutoCompTable (para texto y _screen). Estos valores se guardan en una tabla, de forma predeterminada es AutoComp.DBF.

Se puede emplear más de una tabla para autocompletar, la tabla actualmente activa es la que se corresponde con _screen.AutoCompTable. No puede ser una vista o cursor.

Otras novedades

Controlar el foco

En el evento Valid se puede indicar a qué objeto entregar el foco. Notemos que IntelliSense no muestra el nuevo parámetro Object name, en el comando Return; pero si está comentado en la ayuda. Solo para evento Valid.

Ejemplo:

RETURN ThisForm.Pageframe1.Page2
 RETURN ThisForm.Combo1 
Mejoras en combos y listas

Nueva posibilidad Nodefault en el evento DropDown para Combobox. Hace que no se despliegue la lista.

Se pueden ocultar las barras de scroll para listbox. Para ello empleamos la propiedad AutoHideScrollBar que acepta dos valores 0 – siempre visibles (valor predeterminado) y 1- las barras de scroll son sólo visibles si los elementos que componen la lista son más que el espacio para mostrarlos. Veamos el efecto visual que provoca.

Conclusiones Diseñador de clases y formularios

Invertimos gran parte de nuestro tiempo diseñando clases y formularios. Las novedades de esta versión 9.0 redundan en mejoras de productividad, extensibilidad y funcionalidades para trabajar elementos gráficos.

Administrador de proyectos

Los cambios los podemos resumir en:

  • Cambios de presentación: tipo de letra, para ello basta con hacer Clic derecho en el panel superior de la ventana Propiedades y seleccionar Font. Aparece el cuadro de diálogo Fuente que nos permite configurar la fuente deseada.
  • Nuevas opciones en menú contextual, además algunas opciones han sido renombradas o recolocadas. Veamos algunas diferencias en estas imágenes.
  • Hay dos nuevas funcionalidades importantes:
    • poder modificar una librería de clases, archivo VCX (invoca el Examinador de clases). En las figuras siguientes, que representan la misma ficha del Administrador de proyectos en las versiones VFP 8 y VFP 9 se observa como en VFP 9 se encuentra disponible esta opción.

    • obtener el registro (log) de generación del proyecto / aplicación, aunque no termine de generar. Hasta VFP 8.0 al generar un proyecto / aplicación, (ya sea desde el Administrador de proyecto, el comando BUILD o el método Build del objeto Proyect), el registro de errores no se generaba hasta tanto no estuviera terminada la generación. Con lo cual, si la generación era cancelada por VFP o por el desarrollador no quedaba registro de error con sus correspondientes inconvenientes para la rápida depuración y corrección de los mismos. Además, en VFP 9.0, si al Generar tiene el Depurador activo y si está abierta la ventana de salida del Depurador, pues es mejor todavía, porque se muestran allí los errores. Hay una entrada por cada programa, clase, método que se recorre. Veamos un ejemplo:

    • Se pueden arrastrar archivos de cualquier tipo al Administrador de proyectos, quien se encarga de distribuirlos en las fichas correspondientes.

Administrador del Panel de Tareas

Esta utilidad es bastante poco explotada por los desarrolladores VFP, fue incorporada en la versión 8.0. Se recomienda su utilización por los aportes que puedan reportar en cuanto a organización y productividad de trabajo en equipos.

Se le han agregado al panel Entorno de datos las fichas Field mapping y Forms, contiene configuración de entorno, fichero de recursos, clases para enlazar campos y formularios base.

Nuevo panel - Data Explorer

Permite examinar datos y componentes en Bases de datos VFP, Bases de datos SQL Server, tablas libres VFP, o cualquier otro ODBC u OLE DB que emplee una base de datos vía una conexión ADO. Los que están familiarizados con el Administrador corporativo de SQL Server verán muchas similitudes. La ventaja es que esta herramienta trabaja con todos los tipos de datos (no sólo SQL) y está completamente integrada en el IDE de VFP y siguiendo la tradición, es extensible. Esta herramienta ofrece:

  • Una vía para ver información de recursos de datos VFP, SQL Server y ADO
  • Acceso rápido a los datos como tal
  • Mantenimiento centralizado de múltiples bases de datos sin la necesidad de cambiar a diferentes carpetas, servidores o proyectos
  • Posibilidad de ejecutar consultas como hace el Analizador de consultas de SQL
  • Funcionalidad similar al VS Internet Explorer
  • Valor de uso añadido al arrastrar y soltar. - Si arrastramos una tabla de VFP se escribe la sentencia del comando Select SQL. Pero lo bueno de verdad es cuando se arrastra una tabla de una DBC que se encuentra en un servidor SQL. El texto generado, crea la conexión un CursorAdapter, llena sus propiedades, llena el cursor y prepara todo el tratamiento de errores.

Dedicaremos un escrito aparte a comentar sobre el Panel de tareas en general y sobre este nuevo panel en particular.

Ventanas de código

Aunque ninguno de los cambios son revolucionarios, todos ellos simplifican la tarea diaria de escribir el código. Veamos algunas mejoras.

  • IntelliSense funciona en comandos WITH ... ENDWITH y FOR EACH ... ENDFOR cuando se indica la cláusula AS IntelliSense es válido también para WITH THIS
  • Compilación inmediata: Hace que aparezca un indicativo en la medida que vamos escribiendo y desaparece al terminar de escribir correctamente la línea de código. El indicativo se puede configurar desde el Menú Herramientas – Opciones – Ficha Editor – Background compile activa un combobox con las opciones: color rojo / color gris / subrayado / ninguno (esta última es para desactivas esta funcionalidad: Es utilizada por programadores expertos, ya que la compilación inmediata retrasa la productividad.)
  • Imprimir texto seleccionado: Una nueva opción en el rango de impresión, permite imprimir un texto seleccionado o todo el código.
  • Copiar código con formato: El código impreso en documentos Word mantiene la coloración y estilo. El color en la sintaxis se introdujo en VFP 5.0, en 8.0 se agregó la posibilidad de imprimir código a color; pero solo desde VFP. Ahora el código VFP mantiene su coloración y estilo en documentos, lo que facilita la lectura de código en artículos.

Diseñador de Menús

Se han pedido mejoras en el diseñador de menú y el FoxTeam en lugar de presentar un diseñador nuevo ha brindado la posibilidad de extenderlo con las funcionalidades que necesiten los desarrolladores. La nueva variable del sistema _MENUDESIGNER indica la aplicación o ejecutable que se va a invocar para diseñar el menú, de forma predeterminada es el diseñador de menú de VFP. Se puede modificar desde opciones ficha File Location o mediante código de programa.

_MENUDESIGNER = "d:\WLCProjects\DevTools\MenuDesigner\MenuDesigner.exe"
 _MENUDESIGNER = "d:\AMBYprojects\DevTools\MenuDesigner\MenuDesigner.exe"

La ventaja es que restablecer el diseñador de menú es tan sencillo como

_MENUDESIGNER = SPACE(0)
_MENUDESIGNER = SPACE(0) && ó
 _MENUDESIGNER = "" 

A partir de esto, abra y modifique su menú desde el diseñador nativo o el propio, como ha trabajado habitualmente. Este generador debe devolver un valor lógico

Se han implementado los scripts MENUHIT y MENUCONTEXT FoxCode.DBF, que podemos utilizar para interceptar selecciones hechas en el menú del sistema de VFP y para interceptar menús contextuales de VFP respectivamente.

Depurador

En VFP 9.0, funciona IntelliSense dentro del depurador, es de gran ayuda cuando escribimos el nombre de una propiedad en la ventana Examinar.

Misceláneas

  • Dentro de un Browse se agrega la posibilidad de Buscar siguiente (Find Next) con F3
  • Las propiedades Input y Format se han completado con nuevos caracteres:
    • U: sólo caracteres alfabéticos, directamente escritos en mayúsculas
    • W: sólo caracteres alfabéticos, directamente escritos en minúsculas
    • Z: Muestra el valor en blanco, la novedad es que es aplicable a los datos tipo Fecha.

Conclusiones

Pues ya está, hemos trabajado los datos y creado la interfaz, ahora queremos dejarle el resultado de los datos procesados en forma de informes. Por la importancia del tema Informes en VFP 9.0 dedicaremos otro artículo

Saludos,

Ana María Bisbé York
www.amby.net

7 de noviembre de 2016

Obtener la dirección MAC (Mac Address)

Como obtener la dirección MAC (dirección de la tarjeta Ethernet) ...

Uso:

direccion = MacAddress()
FUNCTION MacAddress
Local pGUID,rGUID,lnSize
Declare integer CoCreateGuid in 'OLE32.dll' ;
  string @pguid
Declare integer StringFromGUID2 in 'OLE32.dll' ;
  string rguid, string @lpsz, integer cchMax
pGUID=replicate(chr(0),16)
rGUID=replicate(chr(0),80)

If "5." $ OS() && 2000/XP
  Declare integer UuidCreateSequential in 'RPCRT4.dll'  string @ Uuid
  Return substr( iif( UuidCreateSequential(@pGUID) = 0 ;
    and StringFromGUID2(pGUID,@rGUID,40) # 0, ;
    StrConv(left(rGUID,76),6), "" ), 26,12)
Else
  Return substr( iif( CoCreateGuid(@pGUID) = 0 ;
    and StringFromGUID2(pGUID,@rGUID,40) # 0, ;
    StrConv(left(rGUID,76),6), "" ), 26,12)
Endif
Cetin Basoz

21 de octubre de 2016

Desglosar cadenas separadas por comas en matrices de Visual FoxPro

Artículo original: Parsing comma-delimited strings into a Visual Foxpro array
http://www.ml-consult.co.uk/foxst-15.htm  
Autor: Mike Lewis
Traducido por: Ana María Bisbé York


Un modo sencillo de desglosar datos en una cadena separada por comas.

En Visual FoxPro (y otros lenguajes de programación) las cadenas delimitadas por comas son una vía sencilla para guardar pequeñas colecciones de datos. Es habitual el uso de estas cadenas para pasar parámetros a procedimientos o funciones, o para guardar valores en propiedades de usuarios o clases. Puede además, importar datos de algunas aplicaciones en forma de un archivo de texto delimitado por comas.

He aquí una vía sencilla para desglosar una cadena separada por comas en VFP 6.0. Utilizando esta sencilla línea de código, puede extraer rápidamente cada elemento separado por la coma y colocarlo en un elemento independiente de la matriz.

nRows = ALINES(laData, STRTRAN(cTest,",",CHR(13)))

En este caso, la cadena delimitada por comas se llama cTest y la matriz se llama laData. Si no existe la matriz, la crea. Luego de ejecutar el código, la variable nRows contendrá la cantidad de elementos en la matriz.

El código realiza dos sencillas operaciones. Primero, la función STRTRAN() convierte cada una de las comas en un carácter de fin de cadena (ASCII 13). La función ALINES() coloca cada "línea" que se ha formado en un elemento de la matriz. ALINES(), que se introdujo en VFP 6.0, está diseñada para extraer párrafos de cadenas de texto muy largas; pero funciona también en el contexto mostrado aquí.

En VFP 7.0 y superior, el código es incluso más sencillo. VFP 7.0 introdujo un parámetro adicional a ALINES(), el que le permite especificar un carácter fin de línea personalizado. Entonces, en lugar de tener que utilizar STRTRAN() para convertir las comas en ASCII 13, puede sencillamente estipular que la coma marca el fin de línea. El código entonces, es el siguiente:

nRows = ALINES(laData, cTest, .F., ",")

El tercer parámetro de ALINES() es un indicador que dice si la "línea" debe ser recortada cuando es copiada en la matriz.

Si desea convertir un archivo de texto entero en una matriz simplemente conviértalo en una cadena de caracteres antes con el código que se muestra a continuación. En VFP 6.0 y superior, la forma más fácil de convertir un archivo en una cadena es con la función FILETOSTR(). Entonces, teniendo un archivo de texto delimitado por comas llamado CLIENTS.TXT, he aquí cómo lo puede desglosar en una matriz llamada aClients:

cClients = FILETOSTR("clients.txt")
nRows = ALINES(aClients,STRTRAN(cClients,",",CHR(13)))

Vea que esta técnica no trabaja si las variables como tal contienen comas. Cuando se almacenan, por ejemplo, nombres y direcciones como variables separadas por comas, por ejemplo, lo habitual es encerrarlas entre comillas, para indicar que al desglosar trate todo este texto entre comillas como un único elemento. Desafortunadamente el código mostrado en este artículo no puede controlar esta situación.

Mike Lewis Consultants Ltd. Julio 2000. Revisado Noviembre 2001.

17 de octubre de 2016

Pedir un nombre de carpeta o archivo a los usuarios

Artículo original: Prompt your users for a file or directory name
http://www.ml-consult.co.uk/foxst-24.htm
Autor:Mike Lewis
Traducido por: Ana María Bisbé York


Esta sencilla clase facilita aún más esta tarea.

Las aplicaciones Visual Foxpro a veces necesitan preguntar al usuario un nombre de archivo o directorio. Habitualmente lo mejor es hacer una combinación de cuadros de texto y botones de comandos. Si el usuario ya conoce el nombre requerido, puede escribirlo en el cuadro de texto. Si no lo conoce. Puede hacer clic en el botón para invocar el diálogo Seleccionar Directorio o Abrir archivo, desde el que puede navegar a la carpeta o archivo en cuestión.

No es difícil crear este tipo de mecanismos. Pero es incluso más fácil si utiliza la clase cmpSelDir de Bárbara Peisch, la que realiza todo el trabajo por usted.

La clase cmpSelDir es una clase compuesta, que contiene un cuadro de texto y un botón de comandos como he descrito antes. Para utilizarla, arrástrela a un formulario. De forma predeterminada, el botón abrirá el diálogo Seleccionar Carpeta. Para abrir en su lugar el diálogo Abrir fichero, cambie la propiedad WhichGet a la palabra File (Vea que es sensible a mayúsculas y minúsculas). Con ambos tipos de diálogo, el nombre de archivo o el directorio que el usuario selecciona será agregado en el cuadro de texto, sobre-escribiendo cualquier texto que estuviese escrito antes. Si el usuario cancela el diálogo, se preserva lo que existía en el cuadro de texto.

La clase ofrece varias propiedades que permiten personalizar el diálogo de formas diferentes. Por ejemplo: se puede asignar a PromptText un texto con una pregunta que puede aparecer en el diálogo, en OpenButtonCaption el texto que va a aparecer en el botón OK en el diálogo Abrir fichero. Aún más interesante, la propiedad FileExtensions puede ser utilizada para llenar el control Tipos de archivos en el diálogo Abrir Archivo. Puede establecer esta lista de extensiones de archivos, con caracteres comodines opcionales, acompañados opcionalmente por descripciones cortas (Ejemplo: "Tablas:DBF; Archivos de texto :TXT, CSV").

Todas estas propiedades tienen parámetros de configuración, por tanto puede ignorar aquellos en los que no está interesado. Para mayor información vea el método ZReadMe.

Y esto es todo lo que hay sobre esta clase. La clase cmpSelDir es una clase elegante, fácil de utilizar, y un ejemplo excelente de una herramienta que realiza un trabajo y lo hace bien.

Sobre la desarrolladora

Barbara Peisch dirige Peisch Custom Software Inc., una consultoría VFP en Carlsbad, CA (USA). Fue editor técnico del libro: "WebRAD: Building Database Websites with Visual FoxPro and Web Connection", y es co-autora de la columna de la revista CoDe "Customers vs Code: Keeping Your Cool with the Essential Component". Barbara es la autora original de la columna KitBox de la revista FoxTalk, y es Microsoft MVP, así como miembro activo del Microsoft Application Developers Forum en Compuserve. Se le puede contactar en barbara@peisch.com o visitando www.peisch.com.

Cómo descargar cmpSelDir

Haga clic en el enlace más abajo para descargar CMPSELDIRFILE.ZIP. Este archivo ZIP contiene una biblioteca de clases, que a su vez contiene la clase cmpSelDir. Contiene además, otra clase llamada cmdFileFind, que es la clase padre de los botones de comandos empleados en la clase compuesta. El tamaño de la descarga es 5 KB.

Descargar ahora http://www.ml-consult.demon.co.uk/cmpseldirfile.zip

Mike Lewis Consultants Ltd. Abril 2002

14 de octubre de 2016

¿Es el Usuario un Administrador?

Rutina para determinar si el Usuario actual de Windows es Administrador.

#DEFINE NO_ERROR 0
 
DECLARE INTEGER IsUserAnAdmin IN shell32
 
DECLARE INTEGER WNetGetUser IN mpr;
    INTEGER lpName, STRING @lpUserName, INTEGER @lpnLength
 
LOCAL lcUser, lnBufsize
lnBufsize = 250
lcUser = Repli(Chr(0), lnBufsize)
 
IF WNetGetUser(0, @lcUser, @lnBufsize) = NO_ERROR
    ? "Nombre de Usuario:", SUBSTR(lcUser, 1, AT(Chr(0),lcUser)-1)
    ? "Es Administrador:", Iif(IsUserAnAdmin()=0, "No", "Si")
ENDIF

Saludos.

Jesus Caro V

10 de octubre de 2016

Tooltiptext multilinea

La propiedad Tooltiptext que ya conocemos pero mostrando varias líneas de texto.

LOCAL CRLF
CRLF = CHR(13) + CHR(10)
This.ToolTipText = "TITULO:" + CRLF + ;
     "Este es un ejemplo de la propiedad tooltiptext" + CRLF + ;
     "que ya conocemos pero con esto la habilitamos" + CRLF + ;
     "para mostrar varias lineas."

Ahora un formulario de ejemplo con tres controles CommandButton con la propiedad ToolTip "multilinea".

PUBLIC oMiForm
oMiForm = NEWOBJECT("MiForm")
oMiForm.SHOW
RETURN

DEFINE CLASS MiForm AS FORM
  AUTOCENTER = .T.
  SHOWTIPS = .T.
  CAPTION = "Ejemplo de tooltip multilinea"
  NAME = "MiForm"

  ADD OBJECT Command1 AS COMMANDBUTTON WITH ;
    TOP = 36, LEFT = 132, HEIGHT = 27, WIDTH = 84, ;
    CAPTION = "Boton 1", NAME = "Command1"

  ADD OBJECT Command2 AS COMMANDBUTTON WITH ;
    TOP = 96, LEFT = 132, HEIGHT = 27, WIDTH = 84, ;
    CAPTION = "Boton 2", NAME = "Command2"

  ADD OBJECT command3 AS COMMANDBUTTON WITH ;
    TOP = 156, LEFT = 132, HEIGHT = 27, WIDTH = 84, ;
    CAPTION = "Boton 3", NAME = "Command3"

  PROCEDURE Command1.INIT
    LOCAL CRLF
    CRLF = CHR(13) + CHR(10)
    THIS.TOOLTIPTEXT = "Ud. esta sobre el Boton 1." + CRLF + ;
      "Esto es un ejemplo de un" + CRLF + ;
      "ToolTip multilineas."
  ENDPROC

  PROCEDURE Command2.INIT
    LOCAL CRLF
    CRLF = CHR(13) + CHR(10)
    THIS.TOOLTIPTEXT = "Ahora el cursor esta sobre el Boton 2." + CRLF + ;
      "Esto es un ejemplo de un ToolTip multilineas."
  ENDPROC

  PROCEDURE Command3.INIT
    LOCAL CRLF
    CRLF = CHR(13) + CHR(10)
    THIS.TOOLTIPTEXT = "Este es el ultimo boton." + CRLF + ;
      "Esto es un ejemplo de un" + CRLF + ;
      "ToolTip multilineas."
  ENDPROC
ENDDEFINE

Saludos.

Jesus Caro V

Problema con HAVING

Artículo original: HAVING gotcha
http://www.foxpert.com/knowlbits_200712_1.htm
Autor: Christof Wollenhaupt
Traducido por: Ana María Bisbé York


Sin la cláusula GROUP BY, HAVING opera de forma similar que una cláusula WHERE, excepto en que el filtro opera en el conjunto resultante:

Create Cursor curSrc (Fld1 I, Fld2 I)
Insert into curSrc values (1, 1)
Insert into curSrc values (2, 2)

Select Fld1, fld1 + 1 as Fld3 ;
  From curSrc ;
  having Fld3 = 2

En este ejemplo, la consulta solamente devuelve aquellos registros donde la expresión "FLD+1" es 2. La capacidad de consultar campos en el conjunto resultante es limitada. Puede consultar solamente los campos que no aparecen en una de las tablas origen. Volviendo a escribir la consulta como esta obtenemos un resultado diferente. La única diferencia en la sentencia es que FLD3 ahora se llama FLD2:

Select Fld1, fld1 + 1 as Fld2 ;
  From curSrc ;
  having fld2 = 2

FLD2 se utiliza solamente en un alias en la lista de campos, todavía la cláusula HAVING opera sobre curScr.Fld2 en lugar de FLD2 del conjunto resultante. Esto puede ocasionar cambios de comportamiento del programa cuando agregue un campo a la tabla con el mismo nombre que una columna calculada.

Una vez que agrega el conjunto resultante con GROUP BY, las reglas cambian ligeramente. Refiriéndose al nombre del campo en el conjunto resultante, si existe un campo con el mismo nombre en cualquiera de las tablas origen, provoca un mensaje de error. En la siguiente consulta, FLD1 + 1 = 2 trabaja en la cláusula HAVING, FLD2=2 no podría:

Select Fld1, fld1 + 1 as Fld2 ;
  From curSrc ;
  group by 1, 2 ;
  having fld1 + 1 = 2

La misma consulta es válida si el nombre del campo no existe en cualquiera de las tablas origen. La siguiente consulta utiliza FLD3 en lugar de FLD2 como nombre del campo:

Select Fld1, fld1+1 as Fld3 ;
  From curSrc ;
  group by 1, 2 ;
  having fld3 = 2

Esta es una situación que podría encontrar cuando hace refactorización y se renombran los campos. Cambiar el nombre del campo local sorprendentemente causa error cuando se ejecuta la consulta.

3 de octubre de 2016

Convertir informe a BMP

La siguiente rutina envía el resultado de un informe a un mapa de bit por página.

Local oListener As ReportListener, nPageIndex
oListener = Createobject("ReportListener")
oListener.ListenerType = 3

cRutaReporte = Home(2)+"Solution\Reports\invoice.frx"

Report Form (cRutaReporte) Preview Object oListener

For nPageIndex=1 To oListener.PageTotal
    cOutputFile = "c:\tmp"+Trans(nPageIndex)+".bmp"
    oListener.OutputPage(nPageIndex,;
    cOutputFile, 105, 0,0,768,1024) && 105=bitmap
Next

Saludos.

Jesus Caro V

28 de septiembre de 2016

Una forma de localizar un form y ponerlo en primer plano mediante código

He averiguado una forma bastante interesante que me permite localizar un form abierto y ponerlo en primer lugar. Tengo una aplicación que ya es bastante grande y no quería usar el consabido 'DO FORM xxxx NAME xVar LINKED' pues no quiero ocupar más espacio en memoria de lo necesario.

VFP tiene en el objeto '_Screen' una matriz que hace referencia a cada uno de los forms abiertos, te tal forma que se puede acceder a dicha matriz. Basándome en esto escribí el siguiente código:

LOCAL lix,lcObjeto,lcTextp
lix = 0
lcObjeto = "Objeto"
DO WHILE !EMPTY(lcObjeto)
  lix = lix +1 
  lcObjeto = "_screen.forms(" + ALLTRIM(STR(lix)) + ")"
  IF TYPE(lcObjeto) = "O"
    lcTextp = &lcObjeto..CAPTION
    IF lcTextp = "Archivo de facturas de compras"
      &lcObjeto..ALWAYSONTOP = .T.
      &lcObjeto..ALWAYSONTOP = .F.
      &lcObjeto..ACTIVATE
      &lcObjeto..pagfrm1.page1.SETFOCUS
    ENDIF
  ELSE
    lcObjeto = ""
  ENDIF
ENDDO

La variable lcObjeto es la que nos va dar acceso al formulario, inicialmente le pongo cualquier valor para que se ejecute el 'DO WHILE ...'. Con cada pasada el 'DO WHILE ...', le incremento la variable 'lix' que es la que nos sirve para indicar cual es el formulario deseado. Pasamos a la variable 'lcTextp' el Caption de cada Form de la matriz y cuando localizamos el que queremos, directamente le cambiamos la propiedad AlwaysOnTop = .T., de esta forma pasa a primer plano, y luego lo ponemos a su valor verdadero. De todas formas por si acaso en el evento 'Activate' del Form tambien tengo puesto el AlwaysOnTop = .F. por si acaso.

Espero que se sirva a alguien, creo que como idea no está mal.

Saludos

Rafael Ramírez

21 de septiembre de 2016

Resolución de SECONDS()

Artículo original: Resolution of SECONDS()
http://www.foxpert.com/knowlbits_200705_5.htm
Autor: Christof Wollenhaupt
Traducido por: Ana María Bisbé York


En Win 9.x, SECONDS() tiene resolución de 1 ms. En los sistemas basados en NT, SECONDS() cambia cada 10 ms, como se puede ver en el programa siguiente:

For t=1 to 100
  ? Seconds()
endfor

En NT se puede cambiar la resolución para SECONDS() utilizando la función API timeBeginPeriod:

Declare long timeBeginPeriod in WinMM.dll long
timeBeginPeriod(1)

Ahora SECONDS() incrementa cada milisegundo:

For t=1 to 100
  ? Seconds()
Endfor

14 de septiembre de 2016

Saber por Api, si podemos Abrir Un Archivo de Forma Exclusiva

Esta Api, nos permite saber si un archivo lo podemos abrir de manera Exclusiva

Si Devuelve .T., esta bloqueado por otra aplicacion, util para saber si esta en uso.

? EstaBloqueado("c:\atisaappreg01.dbf")

Function EstaBloqueado(cArchivo)
 Declare Long _lopen In "kernel32" as lOpen String lpPathName, Long iReadWrite
 Declare Long _lclose In "kernel32" as lClose Long hFile
 Local hFile As Long
 hFile = -1
 hFile = lOpen(cArchivo, 0x10)
 Result = hFile = -1
 lClose (hFile)
 Return Result
Endfunc

Jorge Mota

7 de septiembre de 2016

FONTMETRIC(), TXTWIDTH() y DPI pantallas de Windows

Artículo original: FONTMETRIC(), TXTWIDTH(), and screen DPI in Windows http://www.spacefold.com/colin/posts/2005/12-01FONTMETRICandScreenD.html
Autor: Colin Nicholls
Traducido por: Ana María Bisbé York


Si está tratando de dar tamaño a objetos en un formulario VFP que se correspondan a una fuente específica, tiene que tener en cuenta también la configuración actual de DPI ( Dots Per Inch - Puntos por pulgada) de la pantalla. Considere calcular el tamaño de un objeto Rectángulo que rodee un trozo de texto dado. Conoce las propiedades fontname, fontsize y la cadena de texto; pero no conoce las dimensiones. No hay problemas, puede decir, solo necesito utilizar FONTMETRIC() y TXTWIDTH() para calcular el tamaño del texto en pixeles:

iAvgCharWidth = FONTMETRIC(6, cFontName, cFontSize, "" )
iTextWidth = TXTWIDTH( cString, cFontName, cFontSize, "" ) * iAvgCharWidth

... y diría ... ahora puedo calcular adecuadamente el tamaño de mi rectángulo

No tan rápido. ¿Qué tal si su monitor de Windows estuviera configurado en 120 DPI? El texto se crearía de tamaño diferente. Debe tener esto en cuenta:

*---------------------------------
* Determina la configuración DPI de la pantalla:
*---------------------------------
#DEFINE LOGPIXELSX 88
DECLARE INTEGER GetDeviceCaps IN WIN32API INTEGER HDC, INTEGER ITEM
DECLARE INTEGER GetDC IN WIN32API INTEGER HWND
*DECLARE INTEGER DeleteDC in WIN32API integer HDC
DECLARE INTEGER ReleaseDC IN WIN32API INTEGER HWND, INTEGER HDC
LOCAL hdc, screenDPI
hdc = GetDC(0)
THIS.screenDPI = GetDeviceCaps( m.hdc, LOGPIXELSX )
ReleaseDC( 0, m.hdc )
IF iScreenDPI <> 96
  iTextWidth = INT( (iTextWidth * iScreenDPI)/96 )
ENDIF

Estuve peleándome con este tema recientemente, ocupándome de un bug reportado por alguien que decía que los textos del título del Pageframe se truncaban, si su entorno estaba configurado como 120DPI + "Fuentes grandes"

Bueno, esta mañana leí un post del prestigioso Charles Petzold (http://www.charlespetzold.com/blog/blog.xml#0511250723) sobre Unidades independientes de dispositivo Device Independent Units (DIU) que de alguna manera (y más) explica por qué tenemos que hacerlo de esta forma, porque creo que Visual FoxPro en realidad emplea pixeles reales en lugar de DIU. Este post merece ser leído.

30 de agosto de 2016

Herencia y Polimorfismo con Visual FoxPro

Una de las ventajas que nos dá el uso de clases es el ahorro de programación y el mantenimiento centralizado de códigos que antes podían estár regados (y repetidos) en tu aplicación. Es sencillo, tener tu código lo más general posible, ésto se hace con el uso de propiedades y métodos personalizados por medio de parámetros. Es decir, puede que en cierto lugar establezcas por ejemplo, la tabla base en la que trabajarás, y después, aplicarle métodos con parámetros. Esto es con respecto a herencia, para tratar de que lo que ya escribiste, te sirva en más ocasiones.

Otra función de las clases, es dejar que éstas hagan el código genérico, y después de ello, aplicarle el código específico, esto se hace con la función DoDefault() y el comando NODEFAULT... A continuación un pequeño ejemplo:
oForm = CREATEOBJECT("MyForm")
oForm.SHow(1)
DEFINE CLASS MyForm AS cAbstractForm
   cFormName = "Client Form"
   PROCEDURE cmdAceptar.Click
      DODEFAULT()
      ***** Procedimiento despues de tu codigo base
      MESSAGEBOX("MyForm.Class Message")
   ENDPROC
   PROCEDURE DoAction
     LPARAMETERS tcAction
        MESSAGEBOX("Codigo antes de...")
        DODEFAULT(tcAction)
   ENDPROC     
   PROCEDURE DoExitValidation
      IF DODEFAULT()
         lcMessage = "Reconfirma que quiere salir"
         RETURN (MESSAGEBOX(lcMessage,4+32,this.cFormName)==6)
      ELSE
         RETURN .F.
      ENDIF
   ENDPROC   
ENDDEFINE

DEFINE CLASS cAbstractForm AS FORM
  cExitMessage = "Salir del Formulario"
  ADD OBJECT cmdAceptar AS CommandButton ;
     WITH Top = 200, Left = 150, Caption = "Aceptar",;
          Height = 35
  ADD OBJECT cmdSalir AS CommandButton ;
     WITH Top = 200, Left = 250, Caption = "Salir",;
          Height = 35    
  PROCEDURE cmdAceptar.Click
      Thisform.doAction("Aceptar")
  ENDPROC            
  PROCEDURE cmdSalir.Click
      Thisform.DoAction("Salir")
  ENDPROC
  PROCEDURE DoAction
    LPARAMETERS tcAction
       DO CASE 
          CASE tcAction = "Aceptar"
              *** Simular acción de aceptar
             MESSAGEBOX(tcAction)
          CASE tcAction = "Salir"
             IF  This.DoExitValidation()
                 *** Si ejecutó correctamente su validación, salir del formulario
               This.Release()
             ENDIF
          OTHERWISE 
             MESSAGEBOX("UnHandled Message")   
       ENDCASE        
  ENDPROC
  PROCEDURE DoExitValidation
     RETURN (MESSAGEBOX(this.cExitMessage,4+32,"eSoft Msg") = 6)
  ENDPRO
  PROCEDURE QueryUnload
     IF This.DoExitValidation()
       *** Dejar que haga lo suyo por defecto, salir del form
        DODEFAULT()
     ELSE
        *** Inhibir el comportamiento normal, evita que se cierre el Formulario.
        NODEFAULT
     ENDIF
  ENDPROC
ENDDEFINE
Copia el código anterior, pégalo en tu Command Window, seleccionalo en tu ventana de comandos y presiona ENTER, verás en ejecución un Formulario sencillo, que no tiene más que dos botones uno de aceptar y salir, veamos que es lo que hemos hecho ( y mira que no es poco y quizás te enrede mas...)

He creado una clase abstracta basada en Formulario, en ella se tiene un método general, llamado DoAction, el cual es llamado por los eventos Click de los botones de comando, pasando el parámetro de que acción realizará. También he tenido un método llamado DoExitValidation, éste método se supone hará alguna validación antes de querer salir del formulario, ya sea presionando el botón Salir o haciendo click en el botón de cerrar (esquina superior derecha de todos los formularios), o presionando CTRL+F4, se te das cuenta, el método DoAction al darle como parámetro "Salir" mandará a llamar el método doExitValidation... También tengo un código genérico para el momento de salir, implementado en el evento QueryUnload, mismo que dependerá de DoExitValidation...

Con respecto a nuestra clase heredada de cAbstractForm, le llamamos MyForm, en ella se hace la implementación de todo lo que hemos hecho en la clase base, he puesto varios MessageBox para que se vea el trayecto de cómo se ejecutan las cosas, observemos qué he hecho en nuestras herencias...

MyForm::cmdAceptar.Click() -> Hago que se ejecute cualquiera que sea el código en la clase base, después, ejecuto lo que deseo.

MyForm::doAction() -> Tambien ejecuto código antes de, después hago que se ejecute lo necesario, pasando a la clase base, el parámetro necesario, nótese que ésto afecta el qué se ejecuta en los eventos Click de cmdAceptar y cmdSalir.

MyForm::DoExitValidation() -> Esto es lo más interesante, hago que se ejecute el método base, de ahí, tomo el valor retornado por AbstracForm::DoExitValidation y ejecuto código extra, el cual cambiará el comportamiento de el formulario MyForm, pero no tendrá consecuencias con respecto a lo que necesita el método cAbstractForm::QueryUnload y cAbstractForm::doAction.Salir ya que retorno el mismo tipo de datos...

Así pues, ahi tienes un pequeño ejemplo minimalista de cómo darle herencia y polimorfismo. Espero te sirva.

Espartaco Palma Martínez

25 de agosto de 2016

Usando WSH para enviar secuencias de tecla en VFP

Muchas veces encontramos resuelta cierta funcionalidad que necesitamos para nuestra aplicación en algún software y nos preguntamos ¿cómo podría integrarlo a mi sistema?

En muchos casos la respuesta la encontramos en las API que dicho software pone a nuestra disposición o a veces un control ActiveX que podemos instanciar en nuestros formularios. Pero existen otros casos en los cuales no tenemos una respuesta positiva y tenemos dos alternativas: desarrollamos la funcionalidad requerida o intentamos automatizar el uso de dicho soft.

Veamos entonces como podemos utilizar la potencia del Windows Scripting Host para enviar la secuencia de teclas necesarias a otra aplicación para resolver cierta funcionalidad.

A modo de ejemplo automatizaremos una operación matemática en la aplicación Calculator de Windows. Para ello escriba el siguiente código:

MODIFY COMMAND SendKeys
Copie y pegue el código que se muestra a continuación:
* Listado Completo
Local lnHWND, loWSH

* Declaramos funciones de las API de Windows
DECLARE LONG FindWindow IN WIN32API AS FindWindow STRING @a, STRING @b
DECLARE LONG SetForegroundWindow IN WIN32API LONG

* Buscamos una instancia de la aplicación para obtener su Handler
lnHWND = FindWindow(0, "Calculator")
If lnHWND = 0
    * Si no se está ejecutando, la ejecutamos
    Run /N Calc.EXE
    * Y obtenemos su Handler
    lnHWND = FindWindow(0, "Calculator")
Endif

* Instanciamos el Windows Scripting Host
loWSH = CreateObject("WScript.Shell")

* Enviamos la aplicación a primer plano
SetForegroundWindow(lnHWND)

* Por último enviamos la secuencia de teclas
loWSH.SendKeys("140{+}200")
loWSH.SendKeys("{enter}") 
Secuencias de Escape

En la siguiente línea loWSH.SendKeys("140{+}200") vemos que el signo de suma está encerrado entre llaves. Esto se debe a que está definiendo una secuencia de escape, indicándole al WSH que debe enviar el caracter "+", y debe hacerse de esta forma ya que un signo "+" sin llaves estaría indicando que se debe enviar la pulsación de la tecla SHIFT (por ejemplo: "+casa", enviaría "Casa"). Vea Material Adicional para obtener la lista completa de secuencias de escape.

Conclusión

Si bien el ejemplo es muy sencillo y probablemente nunca se nos va a ocurrir automatizar la calculadora para hacer una suma, vale la pena analizarlo ya que muestra claramente el uso del método SendKeys del WSH.

Material Adicional

Refiérase a http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/wsmthsendkeys.asp para consultar las secuencias de escape definidas para el método SendKeys del WSH.

Acerca del autor

Esteban Bruno nació el 25 de marzo de 1973 en Buenos Aires, Argentina. En el año 1992 se recibió de Analista Programado en la Comisión Argentina de Informática, y en 1998 egresó de la Universidad CAECE con el título de Licenciado en Sistemas. Desde el año 1990 ha trabajado en el área de desarrollo en diferentes empresas y utilizando una amplia gama de lenguanjes (Cobol, C, Clipper, FoxBase, FoxPro desde 2.5 para DOS hasta Visual FoxPro 9, Visual Basic, ASP, Java, etc.) y tecnologías. Es socio del MUG Argentina (Microsoft User Group) y actualmente se desempeña como Analista Funcional en IMR S.A. y dirige el Dpto. de Sistemas de TASSO S.R.L. Contacto: bruno@tasso.com.ar

19 de agosto de 2016

Conversion de Rutas (Path) de largas a cortas y viceversa

Este ejemplo muestra como convertir una ruta larga (formato Windows) a una ruta corta (formato 8.3 MS-DOS) y como revertirlo, llevarla de formato de ruta corta a ruta larga.

*######################################################
*# Llevado de Visual Basic a Visual Foxpro por Int21
*######################################################

*#########################################################
*# Declaracion de las API'S
*#########################################################
DECLARE INTEGER GetShortPathName IN kernel32 ;
STRING @lpszLongPath , STRING @lpszShortPath, INTEGER @cchBuffer
DECLARE INTEGER GetLongPathName IN kernel32 ;
STRING @lpszShortPath, STRING @lpszLongPath, INTEGER @cchBuffer

*#########################################################
*# Convertir una Ruta Larga dentro del equivalente 8.3 de ruta corta
*#########################################################
LOCAL lLen, GetShortPath, sLongPath, La_Ruta
lLen = 0
GetShortPath = ""
sLongPath = "C:\Documents and Settings\Morfeus\Escritorio\Long__-__S1227648262002"
*#### Establecer el buffer para la llamada  a la API
GetShortPath = SPACE(1024)
*#### Llamada a la API, Retirar los Caracteres no deseados y devolver la Ruta
lLen = GetShortPathName(@sLongPath, @GetShortPath, LEN(GetShortPath))
La_Ruta = LEFT(GetShortPath, lLen)
WAIT WINDOWS La_Ruta

*#########################################################
*# Convertir una Ruta Corta en su equivalente de Ruta Larga
*#########################################################
LOCAL lLen, sShortPath, LaRuta
lLen = 0
sShortPath = "C:\Docume~1\Morfeus\Escrit~1\Long__~1"
LaRuta = ""
*#### Establecer el Buffe rpara la llamada a la API
GetLongPath = SPACE(1024)
*#### Llamada a la Api, Retirar los caracteres no deseados y devolver la Ruta
lLen = GetLongPathName(@sShortPath, @GetLongPath, LEN(GetLongPath))
LaRuta = LEFT(GetLongPath, lLen)
WAIT WINDOWS LaRuta

15 de agosto de 2016

Construyendo Objetos MultiCapa en Visual Foxpro

Autor: Jim Booth
Traducción de: Sergio E. Aguirre

El modelo N-Tier

El término N-tier se refiere a los varios niveles de responsabilidad en el diseño de un sistema. La N en N-Tier puede ser cualquier número de 2 en adelante. Un diseño muy común es el modelo 3-Tier. En el modelo 3-tier la aplicación es dividida en 3 capas de responsabilidades distintas, la interface del usuario, la lógica de negocio, y la base de datos. Cada una de estas capas puede ser implementada usando uno o más objetos que se dedican a las responsabilidades de esa capa.

Interface del usuario

La capa interface del usuario contendría todos los aspectos visuales del sistema. Cualquier cosa involucrada en la interacción con el usuario del sistema es manejada por esta capa. Todos los diálogos, cuadros de mensajes, formularios, informes, y otros componentes de interacción con el usuario residirían en la capa interface de usuario de un sistema.

Lógica de Negocio

La capa lógica de negocio tiene la responsabilidad de determinar la forma en que vienen los datos y cómo deben estructurarse para la interface del usuario. También aplica toda regla de validación a los datos provenientes de la interface del usuario antes de mandar estos datos a la base de datos.

La capa lógica de negocio no tiene ningún componente de la interface del usuario en ella como tampoco tiene la responsabilidad de actuar recíprocamente con el usuario. Los problemas percibidos con los datos deben ser comunicados a la capa interface del usuario a través de valores devueltos por métodos y la capa interface del usuario debe mostrar los mensajes al usuario.

Manejo de la Base de Datos

La base de datos es la responsable de manejar el dominio de validación sobre los datos, de actualizar y recuperar los datos en las tablas. Las reglas en la base de datos deben restringirse a sólo aquellas que son de directa aplicación del dominio de validación. "Reglas de Negocio" no son parte de las reglas de la base de datos, en cambio ellas fueron puestas en vigor en la capa lógica de negocio.

Otras Capas

3-Tier no es el único diseño N-Tier. N puede ser cualquier número. Algunas de las cosas que podrían ser consideradas para las capas adicionales son, Interface del Sistema Operativo, Interface de la Red, y los Múltiples Niveles de Capas de Lógica de Negocio.

Por ejemplo, usted puede diseñar un sistema para un banco donde el objeto de lógica de negocio para una Cuenta necesita tener varios formatos diferentes dependiendo de la sección del banco que está usando los datos. En este caso usted puede tener un objeto de lógica de negocio para Cuenta que es genérico para el banco entero, y tiene otros objetos de lógica de negocio que son específicos para las secciones particulares (usando el objeto Cuenta genérico y agregando o restringiendo rasgos basados en los requisitos de la sección).

Ventajas / Desventajas del Diseño N-Tier

Las ventajas del diseño de un sistema N-Tier son múltiples. La siguiente lista muestra algunas de las ventajas.

  1. Usted puede modificar la lógica de negocio sin hacer cambios a la interface del usuario o a la base de datos.
  2. Si lo construyó correctamente, el objeto de lógica de negocio puede ser usado por múltiples interfaces del usuario.
  3. Aisla el conocimiento requerido en cualquier capa dada a esa capa.

Algunas de las desventajas son:

  1. El diseño del sistema es más complejo.
  2. El enlace de datos inherente de Visual FoxPro es indisponible.
  3. La huella de memoria de la aplicación aumenta.

Con estas desventajas, ¿Por qué querría alguien construir un sistema N-Tier? La respuesta es una sola palabra, scalability. El diseño N-Tier puede descascarar a los sistemas sumamente grandes sin compromisos. Por grande estamos refiriéndonos al número de usuarios, el número de diferentes componentes de la interface del usuario, el tamaño de la base de datos, la estructura de la red, y todos los otros problemas de tamaño para una aplicación.

Usando el diseño N-Tier, usted puede diseñar un sistema que pueda manejar múltiples intefaces del usuario divergentes sin tener que volver a escribir la lógica de negocio para cada interface construida. La lógica de negocio puede ser compartida por múltiples interfaces del usuario. Mediante la posibilidad de hacer subclases, las clases de lógica de negocio pueden personalizarse para manejar diferentes servidores de base de datos.

El diseño N-Tier no es apropiado para todos los proyectos, pero cuando se necesita de él es un concepto de diseño sumamente poderoso.

Construyendo Aplicaciones N-Tier en Visual FoxPro

Visual Foxpro puede ser usado para construir cualquiera de las capas comúnes del modelo N-Tier. Visual FoxPro tiene las herramientas para construir interfaces del usuario destacadas. La base de datos nativa de Visual FoxPro es rápida y robusta. Sin embargo, para cada una de estas dos capas hay otras herramientas que también lo hacen bien o mejor. ¿Si la interface necesita imitar a una hoja de cálculo, Excel no sería una mejor opción? Si la base de datos necesita seguridad agregada en la base de datos del servidor, no serían una opción mejor SQL Server o Oracle?

La capa en la que Visual FoxPro tiene ventajas es la capa media, o capa de lógica de negocio, del modelo N-Tier. Debido a la construcción en Lenguaje de Manejo de Datos (DML) de Visual FoxPro, es el primer candidato para manipular datos de un servidor y presentarlos a una interface. También, la posibilidad de crear clases OLE públicas con Visual FoxPro permite interfaces divergentes y bases de datos para usar el mismo objeto de media capa para comunicarse entre sí.

Las Responsabilidades de un Objeto de Lógica de Negocio

Las responsabilidades de un objeto de media capa varían ampliamente. Cosas como la aplicación de la regla de negocio, separación de la interface del usuario y el origen de la base de datos, y proporcionando una sola capa para el acceso a datos para los múltiples servidores de la base de datos está entre las posibles responsabilidades.

Como con muchas otras cosas en el desarrollo orientado a objetos, el diseño del sistema dicta las reales funciones proporcionadas por un objeto de media capa.

Aplicando las reglas de negocio

En cualquier sistema de base de datos hay reglas que controlan que son datos válidos y que son datos inválidos. Estas reglas pueden ser divididas en el dominio de validación y reglas de negocio. Un dominio describe todos los valores posibles que pueden encontrarse en la entidad o atributo al que el dominio aplica. Por ejemplo, el dominio para un campo ciudad puede incluir todos los nombres posibles de ciudades del mundo.

Las reglas de negocio son un subconjunto de un dominio. Las reglas de negocio llevan más allá el límite de valores posibles para ser sólo parte del dominio completo. Con el ejemplo del campo ciudad, quizás nuestra compañía se localiza en Alemania y tiene clientes sólo alemanes. En este caso el dominio para el campo ciudad será todas las ciudades del mundo, pero las reglas de negocio limitarían al campo a sólo ciudades en Alemania.

Mientras que las bases de datos son muy buenas en la aplicación de dominios, ellas pueden ser demasiadas restrictivas si ponen en vigor reglas de negocio. El objeto de lógica de negocio de media capa es un candidato ideal para poner en vigor las reglas de negocio. Visual FoxPro está especialmente preparado para este trabajo porque su motor local de datos permite la creación de diseños de metadata para describir las reglas específicas a ser puestas en vigor. Esto le permite al programador crear reglas de negocios para el manejo de datos que puedan cambiar con el tiempo sin requerir cualquier modificación de código.

Trasladando Datos para la Interface del Usuario

En el diseño 3-Tier la interface del usuario está separada del origen de datos por la capa media, o la capa de la lógica de negocio. Esta separación le permite al programador construir una interface del usuario independiente y capas de almacenamiento de datos. Los perfeccionamientos futuros del sistema pueden incorporar nuevas interfaces del usuario o nuevas tecnologías de almacenamiento de datos sin un cambio que provoque la necesidad de cambiar el otro.

El objeto de media capa realiza el papel de traductor de datos en el formato encontrado en el sistema de almacenamiento de datos a un formato que puede ser usado por la interface del usuario. También traduce los datos desde la interface del usuario a un formato que puede ser guardado por la base de datos.

Con este diseño un cambio en la base de datos requiere sólo que el objeto de media capa sea reforzado, así como un cambio en la interface del usuario también sólo requiere que el objeto de media capa sea reforzado.

Usando Clases de Visual FoxPro para construir un Objeto de Lógica de Negocio

Los ejemplos se pueden descargar aquí.

Exploremos ahora en algo de código para ver una de las muchas maneras que usted puede diseñar un objeto de media capa en Visual FoxPro. La clase de lógica de negocio que nosotros crearemos se va a llamar customer y proporcionará acceso a los datos de ejemplo Fitch Mather que vienen con Visual FoxPro 6.0. La tabla que usaremos es la tabla Stores (Tiendas).

El primer problema con el cual yo traté era qué clase de base usar para crear el objeto customer. Yo escogí usar la clase de base form porque proporciona una sesión de datos privada que protegerá los datos de otras instancias del objeto customer.

A la clase customer yo agregué una propiedad llamada oRDS para usarse como una referencia a un Control de Datos RDS. Yo usé RDS como la metodología de acceso a datos para que la clase customer pueda ser fácilmente modificada para acceder a otros sistemas de base de datos. El control de Datos RDS se crea en el Init de la clase Customer. El código del método Init se describe debajo.

 
* Creo el Control de Datos RDS
This.oRDS = CreateObject("rds.datacontrol")
* Verificamos si la creación tuvo éxito 
If Type("This.oRDS") <> "O" 
   * Si es no devuelvo .F. 
   Return .F. 
Else 
   * Si tuvo éxito fijo algunas propiedades del datacontrol
   With This.oRDS 
      * Establezco el nombre del origen de Datos
      .Connect = "dsn=dsnFitchMather" 
      * Prepare la declaración SQL para ejecutarla
      .SQL = "Select * from stores" 
      * Fijo la ejecución síncrona
      .ExecuteOptions = adcExecSync 
      * Fijo el saque en el fondo 
      .FetchOptions = adcFetchBackground 
      * Ejecuto la consulta 
      .Refresh 
   EndWith 
EndIf 

Los comentarios en el código anterior son autoexplicativos. Una vez que el objeto customer existe tiene el control de Datos RDS dentro de él y el Control de Datos RDS está sacando los datos.

NOTA:

Las constantes referidas en el código anterior se toman de un archivo llamado adcvbs.h. Los contenidos de este se listan debajo.

*-------------------------------------------------------------------- 
* Microsoft ADC 
* (c) 1997 Microsoft Corporation. All Rights Reserved.
* ADO constants include file for VBScript
*-------------------------------------------------------------------- 
*---- enum Values ---- 
#Define adcExecSync 1 
#Define adcExecAsync 2 
*---- enum Values ---- 
#Define adcFetchUpFront 1 
#Define adcFetchBackground 2 
#Define adcFetchAsync 3 
*---- enum Values ---- 
#Define adcReadyStateLoaded 2 
#Define adcReadyStateInteractive 3 
#Define adcReadyStateComplete 4 

A esta clase yo he agregado varios métodos que se listan en la siguiente tabla.

Método

Propósito

GetValue

Usado para obtener el valor de un campo

SetValue

Usado para establecer el valor de un campo

MoveFirst

Mueve al primer registro en el RecordSet

MoveLast

Mueve al último registro

MoveNext

Mueve al próximo registro

MovePrev

Mueve al registro anterior

Requery

Refresca el Control de Datos mediante la re-ejecución de la consulta SQL. El método requery está para hacer que la sintaxis del objeto customer sea similar a la sintaxis nativa de VFP nativa para el requerying de una vista.

RevertChanges

Descarta los cambios pendientes de los datos

SaveChanges

Graba los cambios pendientes de los datos

Las siguientes secciones presentarán el código que está en estos métodos.

GetValue

LPARAMETERS PcField
* Verifico por un parámetro válido 
IF NOT EMPTY(pcField) AND VARTYPE(pcField) = "C" 
   * Verifico si es un nombre de campo válido para este objeto
   IF LOWER(pcField) $ "store_name~store_add1~store_addr2~store_addr3~" + ;
      "store_city~store_id~store_desc~store_phone1~" + ; 
      "store_state~store_type~store_zip" 
      * Demanda de un campo válido para este objeto, así que devuelvo el valor 
      RETURN THIS.oRDS.Recordset.Fields(pcField).Value 
   ENDIF 
ENDIF 
* Demanda de un campo inválida 
RETURN .NULL. 

SetValue

LPARAMETERS PcField, pxValue
IF NOT EMPTY(pcField) AND VARTYPE (pcField) = "C" 
   IF LOWER(pcField) $ "store_name~store_add1~store_addr2~store_addr3~" + ; 
      "store_city~store_id~store_desc~store_phone1~" + ; 
      "store_state~store_type~store_zip" 
      IF VarType(pxValue) = "C" 
         pxValue = ALLTRIM(pxValue) 
      ENDIF 
      THIS.oRDS.Recordset.Fields(pcField).Value = pxValue 
      RETURN .T. 
   ENDIF 
ENDIF 
RETURN .F.

MoveFirst

ThisForm.oRDS.RecordSet.MoveFirst
RETURN 1 

MoveLast

ThisForm.oRDS.RecordSet.MoveLast
RETURN 1

MoveNext

LOCAL LnRet
lnRet = 1 
ThisForm.oRDS.RecordSet.MoveNext 
If ThisForm.oRDS.RecordSet.Eof 
   lnRet = -1 
   ThisForm.oRDS.RecordSet.MoveLast 
EndIf 
RETURN lnRet 

MovePrev

LOCAL LnRet
lnRet = 1 
ThisForm.oRDS.RecordSet.MovePrevious 
If ThisForm.oRDS.RecordSet.Bof 
   lnRet = -1 
   ThisForm.oRDS.RecordSet.MoveFirst 
EndIf 
RETURN lnRet

Requery

ThisForm.oRDS.Refresh

RevertChanges

THISFORM.Requery()

SaveChanges

ThisForm.oRDS.SubmitChanges()
THIS.Requery()

Usted puede preguntarse por qué yo he creado todos estos métodos para hacer cosas que podría haber hecho refiriéndome directamente al Control de Datos RDS. La respuesta es que esto proporciona una interface de desarrollo para el objeto de negocio que es independiente de la naturaleza del objeto de datos. Yo puedo hacer subclases de esta clase y escribir código que con datos locales de VFP, o ADO en lugar de RDS, o ODBC a través de vistas remotas. Ninguno de éstas modificaciones requeriría que cualquier código, en el nivel de UI, sea cambiado en absoluto.

Usando el Objeto de Negocio

El proyecto también incluye un formulario de VFP que demanda esta clase de negocio. El nombre del formulario es Customer.scx. Examinemos el código de este formulario que usa la clase de negocio.

En este formulario se agrega una propiedad llamada oBusObj. En el evento Load del formulario el código será el siguiente.

THISFORM.oBusObj = NewObject("Customer")

Esto crea una instancia del objeto customer y almacena una referencia a él en la propiedad oBusObj del formulario. En el Refresh del textbox número de tienda este es el código:

THIS.Value = THISFORM.oBusObj.GetValue("store_id")

Este código llama al GetValue del objeto de negocio y fija la propiedad Value del textbox para ser el valor devuelto por el método. El evento Valid para el mismo textbox es:

THISFORM.oBusObj.SetValue("store_id",THIS.Value)

Que escribe el Valor devuelto a la fuente de datos del objeto de negocio.

El evento Click para el botón Top es:

THISFORM.oBusObj.MoveFirst()
THISFORM.Refresh()

¿Está empezando a ver un patrón aquí? ¿Puede apreciar lo fácil que es usar este objeto de negocio para manejar el acceso de datos? El otro código en el formulario es similar salvo que se llaman métodos del objeto de negocio.

Creando un servidor ActiveX desde las clases de Visual FoxPro

Así que, ¿Cuál es el gran trato? ¿Por qué es mejor usar un objeto separado para manejar el acceso a datos cuándo el formulario tiene que un entorno de datos maravilloso y controles que pueden enlazar directamente los datos?

La respuesta a estas mentiras es la palabra scalability. Scalability es la habilidad de un sistema de crecer incluir nuevos rasgos con el tiempo, grandes volúmenes de datos, interfaces del usuario adicionales, y otros perfeccionamientos. Si usted demanda el entorno de datos de un formulario de VFP por acceder a los datos, entonces usted se limita a usar VFP para construir la interface del usuario o usted necesitará crear las mismas capacidades de acceso a datos en alguna otra herramienta.

¿Qué pasa si las mismas necesidades de acceso a datos estén disponibles a VFP y a Microsoft Excel? La respuesta es hacer a la clase customer como una clase OLE Pública y construir un COM DLL con ella incluida. Para hacer una clase OLE Pública, abra el diseñador de clases y luego elija Información de clase... en el menú Clase. Verifique que esté marcada la casilla OLE público.

Para construir el COM DLL abra el proyecto y escoja la opción Información del proyecto... del menú Proyecto, seleccione la etiqueta Servidores y establezca sus opciones de la clase (estas opciones están bastante bien documentadas en el archivo de ayuda). Guarde esas opciones y entonces escoja Generar y seleccione la opción Servidor COM (dll) y pulse el botón Aceptar. Esto va a generar el COM DLL con su clase COM servidor en él y registrará la DLL en su máquina. Para otros para usar la clase DLL necesitará ser instalada y registrada en sus máquinas, esto puede hacerse como parte del proceso de Instalación para su aplicación.

Una vez que usted ha hecho esto puede modificar sus formularios de VFP para que usen la clase COM. Simplemente cambie el evento Load para que sea:

THISFORM.oBusObj = NewObject("BusObj.Customer")

Donde BusObj es el nombre del archivo DLL que usted creó.

Usando Múltiples Interfaces del Usuario en el Diseño N-Tier

La ventaja de este diseño es que el mismo objeto de negocio puede ser usado por múltiples interfaces del usuario diferentes permitiendo una definición de clase para controlar el acceso a datos para cada UI que su sistema usa. Aunque su aplicación sólo pueda limitarse a formularios de VFP al principio, siguiendo este diseño N-Tier le permitirá, de manera más fácil, agregar otras interfaces al sistema en el futuro (haciendo el sistema escalable).

Usando Microsoft Excel para la Interface del Usuario

Aquí hay un ejemplo de una macro de Microsoft Excel que usa la misma clase de negocio para llenar con datos una hoja de cálculo.

Option Explicit
Public Dummy As Variant
Public oCustomer As Object
Sub nTier()
   ' nTier Macro
   '
   Dim lnRet As Integer
   Set oCustomer = CreateObject("BusObj.Customer")
   ActiveSheet.Cells(1, 1) = "Store ID"
   ActiveSheet.Cells(1, 2) = "Store Name"
   ActiveSheet.Cells(1, 3) = "Store City"
   lnRet = Refresh()
   ' frmRefresh.Show
End Sub
Public Function Refresh()
   ' nTier Macro
   ' Refreshes the contents of the business logic object and the sheet.
   Dim lnRet As Integer
   Dim lnRow As Integer
   lnRet = oCustomer.MoveFirst()
   lnRow = 2
   Do While lnRet > 0
      ActiveSheet.Cells(lnRow, 1) = oCustomer.GetValue("store_id")
      ActiveSheet.Cells(lnRow, 2) = oCustomer.GetValue("store_name")
      ActiveSheet.Cells(lnRow, 3) = oCustomer.GetValue("store_city")
      lnRet = oCustomer.MoveNext()
      lnRow = lnRow + 1
   Loop
   Refresh = 1
End Function

Esta macro creará una instancia de la clase customer y entonces llenará las filas y columnas de la hoja con datos del objeto de negocio.

Resumen

Hay muchas charlas alrededor del diseño de sistemas N-Tier. Algunas personas son fuertes defensoras de usar N-Tier para todo, en cambio otras personas sienten que el N-Tier es excesivo en muchos lugares. Mi opinión es que algo que yo pueda hacer para mejorar con el tiempo un sistema para mi cliente es bien esfuerzo bien hecho.

El diseño N-Tier me da la capacidad de manejar datos a través del uso de clases de las que se pueden hacer subclases y pueden ser especializadas dentro de VFP y al mismo tiempo pueden hacer que esas clases estén disponibles a otras herramientas de desarrollo que manteniendo el lugar solo modificando el acceso de datos. Esta sola situación es uno de los mayores beneficios logrados a través del diseño N-Tier. Si el cliente cambia el servidor de la base de datos a otra, hay sólo un lugar para hacer los cambios necesarios y todas las interfaces del usuario se actualizarán.

Envíe un email a Jim Booth con preguntas o comentarios sobre esta información.

10 de agosto de 2016

Operador XOR en VFP

En VFP no existe el operador XOR, podemos utilizar el siguiente truco para así poder usarlo.
Definimos XOR como la desigualdad #

#DEFINE XOR #
? "Tabla de verdad del operador XOR"
? .F. XOR .F.
? .F. XOR .T.
? .T. XOR .F.
? .T. XOR .T. 

Mostramos para mas ayuda la tabla de verdad de los opradores AND, OR y XOR

VALORESRESULTADOS APLICANDO:
ANDORXOR
FALSEFALSEFALSEFALSEFALSE
FALSETRUEFALSETRUETRUE
TRUEFALSEFALSETRUETRUE
TRUETRUETRUETRUEFALSE

3 de agosto de 2016

Obtener la dirección IP local con Winsock

************************************************************
* Clase: GET_IPADDRESS
*
 Devuelve la dirección IP local
*
* Parametros: ninguno
*
* Ejemplos:  ipdir=get_ipaddress()
*
* Retorno: Dirección IP o cadena vacia si no está instalado el WinSock
*
* Nota: Adaptado de John Harvey
*
************************************************************
LOCAL lcret
IPSocket = CreateObject("MSWinsock.Winsock")
IF TYPE('IPSocket')='O'
 lcret = IPSocket.LocalIP
ELSE
 MESSAGEBOX("Winsock no está instalado!")
 lcret = ""
ENDIF
RETURN lcret
************************************************************
Pablo Roca

11 de julio de 2016

Normas de codificación en Visual FoxPro

Texto original: Visual FoxPro Coding Standards
http://www.craigberntson.com/Articles/kb015.htm
Autor: Craig Berntson
Traducido por: Ana María Bisbé York
Última actualización: Septiembre 26, 2003


Resumen

Aplicar buenas normas de codificación es importante para el desarrollo de cualquier proyecto; pero es particularmente importante cuando muchos desarrolladores están trabajando en el mismo proyecto. Tener establecido pautas de codificación contribuye a garantizar que el código es de alta calidad, con pocos errores y de fácil mantenimiento.

Estas normas o pautas se basan en años de desarrollo de aplicaciones con Visual FoxPro y de aprendizaje de cuáles técnicas de codificación trabajan mejor. Además, muchos conceptos han sido adaptados a partir de Code Complete (Microsoft Press, ISBN 1-55615-484-4) escrito por Steve McConnell. Este libro, es considerado una de las guías más importantes en prácticas de codificación. Puede que usted no esté de acuerdo con éstas normas, no hay problemas. Para mí, estas normas han funcionado bien.

Normas de codificación

Convenciones de variables.

No utilice guiones bajos en nombres de variables. Mezclar mayúsculas y minúsculas mejora la legibilidad. La primera letra de la variable debe indicar su alcance y debe ser siempre minúscula. Trate de evitar el uso de variables públicas (PUBLIC) y privadas. En su lugar utilice propiedades de los objetos de su aplicación, propiedades de formularios y variables locales.

  • l - Local
  • g - Global
  • p - Private
  • t - Parameter

La segunda letra indica el tipo de dato.

  • c - Character
  • n - Numeric
  • d - Date
  • t - DateTime
  • l - Logical
  • m - Memo
  • a - Array
  • o - Object
  • x - Indeterminate

Ejemplos de nombres de variables válidos:

lcFirstName
tdBeginDate

Tenga en mente el alcance de la variable. Preferiblemente deben utilizarse variables locales (LOCAL). Las variables públicas (PUBLIC) deben evitarse tanto como sea posible. Las declaraciones de variables, tales como: LOCAL lcMyVar se deben colocar todas al inicio de la rutina a ejecutar, en lugar de dispersarlas por el código. Declare todas las variables al inicio de la rutina, en lugar de entremezclarlas por todo el código y asigne su valor predeterminado.

Forma incorrecta:

LOCAL lnMyNum
lnMyNum = 12

LOCAL lcMyString
lcMyString = "ABCD"

LOCAL lnCounter
lnCounter = 0

Forma correcta:

LOCAL lnMyNum, lcMyString, lnCounter

lnMyNum = 12
lcMyString = "ABCD"
lnCounter = 0

Convenciones para nombrar objetos.

Las primeras tres letras del nombre de un objeto deben ser utilizadas para indicar el tipo del objeto.

  • chk - Check box
  • cbo - Combo box
  • cmd - Command button
  • cmg - Command Group
  • cnt - Container
  • ctl - Control
  • cus - Custom
  • edt - Edit box
  • frm - Form
  • frs - Form set
  • grd - Grid
  • grc - Grid Column
  • grh - Grid Column Header
  • img - Image
  • lbl - Label
  • lin - Line
  • lst - List box
  • olb - OLE Bound Control
  • ole - OLE Object como un ActiveX Control
  • opg - Option Group
  • pag - Page
  • pgf - Pageframe
  • sep - Separator
  • shp - Shape
  • spn - Spinner
  • txt - Text box
  • tmr - Timer
  • tbr - Toolbar

Normas para el código fuente:

  1. Utilice abundantes espacios en blanco. Hará más legible su código.
  2. Utilice tabuladores, en lugar de espacios para indentar.
  3. Los comandos y funciones de Visual FoxPro deben escribirse en mayúsculas y con la totalidad de sus letras, el resto del código debe estar escrito con combinación de mayúsculas y minúsculas. Mantenga líneas cortas, tanto como sea posible, para evitar desorden (line wrap) durante la impresión. Si una línea de código, ocupa físicamente más de una línea, utilice las expresiones de unión como primer carácter de la línea siguiente. Las expresiones de unión son del tipo +, AND, OR, NOT, etc. Recuerde además, que la línea debe ser tan válida como si fuera una sola línea. Coloque un espacio antes del punto y coma.

Ejemplos de malas separaciones de líneas:

lcCommand = "Hoy es Miércoles, 16 de Octubre de 2003" + ;
"Son las 2:00 PM"
 
IF ldBeginDate >= DATE() OR ;
   ldEndDate >= DATE()

Ejemplos de buenas separaciones de líneas:

lcCommand = " Hoy es Miércoles, 16 de Octubre de 2003" ;
   + "Son las 2:00 PM"
 
IF ldBeginDate >= DATE() ;
   OR ldEndDate >= DATE() 
  1. Por lo visto, cada uno combina las mayúsculas y minúsculas de forma diferente. Aunque no existe vía correcta o incorrecta en este aspecto, si hay algunas normas que ayudan a la legibilidad. Además, utilice expresiones CASE en lugar de IF, cuando parezca que se pueden agregar posteriormente más opciones, incluso si existen sólo dos opciones en el momento en que es escrito el código o, para liberarse de las instrucciones IF, ELSE, IF. Separe cada CASE por una línea en blanco. El comentario para el CASE debe ir por debajo del mismo.

Forma incorrecta:

DO CASE
  * Este es el comentario para el caso 1
  CASE lnCount = 1
    lcRetVal = "Uno"
  * Este es el comentario para el caso 2
  CASE lnCount = 2
    lcRetVal = "Dos"
  * Este es el comentario para otherwise 
  OTHERWISE
    lcRetVal = "Otro"
ENDCASE

Forma correcta:

DO CASE
  CASE lnCount = 1
    * Este es el comentario para el caso 1
    lcRetVal = "Uno" 
  CASE lnCount = 2
    * Este es el comentario para el caso 2
    lcRetVal = "Dos" 
  OTHERWISE
    * Este es el comentario para otherwise 
    lcRetVal = "Otro"
ENDCASE 
  1. Trate de evitar macro sustitución. A veces la macrosutitución es la única vía para lograr algo. Asegúrese de documentar, por qué utiliza macrosustitución y cuál es el propósito del código. En la mayoría de los casos, la macrosustitución hace que el código sea menos legible. Utilice la función EVALUATE(). Si es posible, pero nuevamente los comentarios son importantes para ayudar a leer el código.
  2. Evite utilizar STORE.
  3. Utilice "[]" en lugar de paréntesis en los arreglos. Así, el código es más legible.
  4. Coloque espacios entre los operadores matemáticos. Esto mejora la legibilidad.
  5. Utilice paréntesis cuando llame métodos o funciones, incluso, si no pasa ningún parámetro.
  6. Evite el uso de m. a la izquierda del signo igual. Esto mejorará el rendimiento.
  7. Al concatenar cadenas, coloque la variable a la izquierda del signo +. Esto mejorará el rendimiento.

Normas para comentar código

Los comentarios son parte importante de cualquier aplicación. Utilícelos abundantemente. Los comentarios deben explicar por qué se han hecho algunas cosas e indicar cuáles son las líneas de código que están afectadas. Debe explicar solamente cómo se hizo algo, en caso de estar utilizando complejos algoritmos o cálculos.

No emplee comentarios al final de las líneas con &&. Cada comentario debe ocupar una línea propia.

Encabezados de programa, método y procedimiento.

Los encabezados de programa, método y procedimiento deben indicar el nombre de la rutina, la fecha en que fue creada originalmente, el autor y una descripción del objetivo del procedimiento o método. En caso de existir parámetros y valores devueltos, incluya una descripción de los mismos. Para los métodos, incluya la jerarquía de objetos.

Ejemplo 1:

*********************************************************
* Método........: frmQueue.cmdNext.Click
* Descripción...: Displays the next item in the selected queue
* Fecha.........: 01-Oct-2001
* Autor.... ....: Fred Flintstone
*********************************************************
* Modification Summary
*
*********************************************************

Ejemplo 2:

*********************************************************
* Función......: CalcIntrest
* Descripción...: Calculate the interest in dollars on the loan
* Parámetros....: tnBalance: Required: The balance amount
* : tnRate: Required: The interest rate to apply
* Devuelve......: Numeric: The dollar amount of the interest
* Fecha.........: 01-Oct-2001
* Autor.........: Bullwinkle J. Moose
*********************************************************
* Modification Summary (Resumen de modificaciones)
*
********************************************************* 

Comentar modificaciones

Es importante poner comentarios al hacer modificaciones para saber cuáles fueron las modificaciones realizadas, y por qué las realizó. La sección Modification Summary (Resumen de modificaciones) en el encabezado explicará por qué, cuándo y por quién fueron realizadas las modificaciones. En cada lugar del código, donde fue realizada una modificación, debe comentar el código viejo e indicar cuál es el código nuevo que fue añadido. Cada modificación debe ser numerada. Los cambios pueden ser borrados después de un año; pero el Resumen de modificaciones debe permanecer.

Ejemplo:

*********************************************************
* Modification Summary
*
* /01 05-Oct-2001 George Jetson
* Changed interest calculation to include a date range factor.
* /02 10-Oct-2001 Tennessee Tuxedo
* 1. Added code to handle interest on widgets. The calculation is different.
* 2. Changed the return value from numeric to character.
********************************************************* 
*/01 lcNote =  "This is the old line commented out "
*/01
lcNote =  "This is the new line of code. " 
*/02-1 lnInterest = a * b
*/02-1 lnInterest = lnInterest / 43 
*/02-1 - Begin - Multiple lines are being added, so indicate they start here
IF UPPER(tcIntType) =  "WIDGETS "
  lnInterest = a * c
ELSE
  lnInterest = a * b
  lnInterest = lnInterest / 43
ENDIF
*/02-1 - End

Las líneas de comentario deben indentarse al mismo nivel del código.

Este ejemplo es incorrecto:

IF lnTotalInterest = 0
*/01 – Begin
  FOR lnCount = 1 TO lnTotal
    lnTotalInterest = lnTotalInterest + laLoans[lnCount, 2]
  ENDFOR
*/02 – End
ENDIF

Este ejemplo es correcto:

IF lnTotalInterest = 0
  */01 – Begin
  FOR lnCount = 1 TO lnTotal
    lnTotalInterest = lnTotalInterest + laLoans[lnCount, 2]
  ENDFOR
  */02 – End
ENDIF

Al comentar líneas que continúan, hay que comentar cada línea física.

Incorrecto:

*/01 lcString =  "Esta cadena contiene mucho texto porque es un  " ;
  +  "ejemplo de una línea verdaderamente larga "

Correcto:

*/01 lcString =  "Esta cadena contiene mucho texto porque es un  " ;
*/01 +  "ejemplo de una línea verdaderamente larga " 

Normas para interfaz de usuario

Normas generales para interfaz de usuario (UI)

Cuando sea posible, la interfaz de usuario debe cumplir con las directivas de Windows Standards descritas en el libro  "Microsoft Windows User Experience ", Microsoft Press, ISBN 0-7356-0566-1. Este libro se encuentra disponible en: http://msdn.microsoft.com/library/en-us/dnwue/html/welcome.asp. Tenga en mente todo el tiempo al usuario. Mientras más sencilla sea la funcionalidad para el usuario, mejor será la aplicación. Esto puede significar que el código que está detrás de esta funcionalidad es más complejo.

Formularios de entrada de datos

  1. Todos los campos aptos para entrada deben utilizar Select On Entry.
  2. Los campos numéricos deben ser formateados con comas y signos negativos cuando sea necesario.
  3. Si un campo es de sólo lectura, establezca la propiedad TabStop en .F., Establezca la propiedad ReadOnly a .T. o la propiedad Enabled a .F., según sea el caso. La propiedad ReadOnly se utiliza cuando el campo nunca es modificable. La propiedad Enabled se utiliza cuando el campo es modificable cuando se cumple una determinada condición (es evaluada a .F.)
  4. Utilice la barra de estado para mostrar un mensaje al usuario que indique el propósito del campo.
  5. Muestre siempre los valores predeterminados, en los casos en que se aplique.
  6. Inhabilite (Enabled = .F.) los objetos cuando sea necesario. Esto proporciona indicación visual al usuario de que el objeto no puede ser modificado en ese momento.

Mensajes

  1. No utilice WAIT WINDOW para mostrar mensajes importantes al usuario. Es muy fácil que el usuario no note el mensaje. En su lugar utilice MESSAGEBOX. Incluya siempre el icono apropiado en el cuadro de mensaje (message box). Evite la utilización del parámetro TIMEOUT, ya que el usuario puede perder información importante.
  2. Utilice la barra de estado para mostrar ayuda en línea para el objeto actual, ya sea en un formulario o un menú.

Objetos de formulario

  1. Text box. (cuadros de texto) El cuadro de texto es el control más comúnmente utilizado. Puede contener valores de caracteres, numéricos o fecha.
  2. Check box. (casilla de verificación) La casilla de verificación es un control sencillo que está establecido en ON u OFF. Son utilizadas típicamente para indicar un estado de Sí o No.
  3. Command button. (Botón de comando) El botón de comandos es utilizado para iniciar una acción. Los botones más comúnmente utilizados son OK (Aceptar) y Cancel (Cancelar). Algunas veces los botones de comandos son agrupados en los command group (grupos de comandos). Intente evitar los grupos de comandos, los botones individuales son más fáciles de mantener.
  4. Option button. (Botón de opción) En ocasiones se les llama radio button. Se utiliza para indicar que el usuario puede seleccionar una opción de un grupo de opciones. Los botones de opciones son frecuentemente agrupados en Option group (grupo de opciones). Esto simplifica el código para el proceso de selección. Se recomienda que los grupos de opciones se utilicen en lugar de los botones de opciones. Los grupos de opciones deben ser preferiblemente ordenados verticalmente antes que horizontalmente.
  5. Drop-down list. (Lista desplegable) Una lista desplegable permite al usuario hacer una selección de una lista de varios objetos, como con un botón de opción. Sin embargo, la lista desplegable requiere de menos espacio en pantalla.
  6. Combo box. (Cuadro combinado) Los cuadros combinados se identifican como un textbox en una flecha hacia abajo. Se llama cuadro combinado, porque es una combinación de un cuadro de texto y una lista desplegable. El usuario puede escribir un nuevo valor o seleccionarlo de la lista.
  7. List box. (Cuadro de lista) Un cuadro de lista puede ser utilizado uno o varios objetos de una lista.
  8. Spinner. Un control spinner es una especie de textbox con flechas hacia arriba y hacia abajo. Es utilizado normalmente para datos numéricos. El número aumenta o disminuye al hacer clic en las flechas o el usuario puede introducir un valor específico.
  9. Edit box. (Cuadro de edición) Un cuadro de edición se comporta de forma parecida al textbox; pero normalmente es utilizado para campos memo. Un editbox puede tener barras de desplazamiento y deslizamiento de filas (word wrap)

Copyright © 2001-2004, Craig Berntson. All Rights Reserved.

9 de julio de 2016

Dígito verificador CURP (México)

Rutina para calcular el dígito verificador de la CURP (México) a partir de los 17 caracteres iniciales de la misma.

* Dígito Verificador CURP

Function _Curp(cCurp)

  cCaracteres='0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ'
  nFactor=19
  nSuma=0

  FOR nIndice=1 TO LEN(cCaracteres)

    cCaracter=SUBSTR(cCurp,nIndice,1)
    nPos=AT(cCaracter,cCaracteres)
    nFactor=nFactor-1
    nSuma=nSuma+nPos*nFactor
 
  ENDFOR 

  nDigito=10-MOD(nSuma,10)
  nDigito=IIF(nDigito=10,0,nDigito)
  cCurp=cCurp+TRANSFORM(nDigito)

  RETURN cCurp
ENDFUNC

Jesus Caro V

6 de julio de 2016

El huevo o la gallina, ¿Propiedades o métodos?

Artículo original: Egg or Chicken, Properties or Methods?
http://www.foxpert.com/knowlbits_200701_6.htm
Autor: Christof Wollenhaupt
Traducido por: Ana María Bisbé York


En Visual FoxPro se pueden utilizar expresiones para iniciar propiedades. Estas expresiones son evaluadas una vez que carga la primera vez la clase en la memoria. El resultado se guarda en el objeto de clase. Para crear una expresión para una propiedad, puede, o entrar el signo igual en la ventana Propiedades seguido por la expresión, o presionar el botón "fx" que está junto al botón Zoom, en la ventana Propiedades.

He mostrado en varias ocasiones que estas expresiones pueden incluir THIS. La referencia apunta al objeto clase, no al objeto que está creando. De hecho, THIS ni siquiera apunta a un objeto de la misma clase. Si solamente accede a las propiedades del objeto clase, no notará la diferencia.

No obstante, si intenta llamar a un método debe tener en cuenta que las propiedades son evaluadas antes que los métodos. ¿Esto qué significa? Cree una clase "Level1" basada en Form y guárdela en Level1.VCX. Agregue el método "CallMethod" con el siguiente código:

Activate Screen
? "Llamada ", This.Class

Ahora, añada la propiedad nTest y asigne la siguiente expresión:

=This.CallMethod()*2

En la ventana de comandos escriba:

Clear All
Release All
Clear
ox = NewObject("Level1","Level1")
? ox.nTest

Notará dos cosas. La pantalla está vacía y nTest es .F., no 2. La razón para esto es que, en el momento en que es evaluada la expresión, aún no ha sido adjuntado el código al método. CallMethod existe; pero está vacío. Ahora, permítame avanzar a un punto superior. Crear un programa ShowInfo.prg con el siguiente contenido

Lparameters toRef
? toRef.Class
toRef.CallMethod()
Return 1

Cree una nueva clase Level2 que hereda de Level1. Almacene la nueva clase en Level2.vcx. Sobreescriba CallMethod con este código:

Activate Screen
? "Subclase llamada ", This.Class

Ahora active la ventana Comandos y escriba:

Clear All
Release All
Clear
ox = NewObject("Level2","Level2")

Esta vez puede ver la siguiente salida:

Level2
Called Level2

Cuando se carga la clase, ya está claramente llamado el método ShowInfo. La llamada del método también trabaja, al menos en alguna medida. En lugar de ejecutar el código en la clase Level2, ahora ejecuta el código desde la clase Level,1 que no ha sido ejecutada cuando se instancia el Level1.

Lo que ocurre es que cuando instancie Level2, Visual FoxPro, crea primeramente un objeto para Level1. Esto ocurre en dos pasos. Primero, son evaluadas todas las propiedades. Luego, se añaden todos los métodos. Como se ha pedido Level2, Visual FoxPro, crea luego el objeto para Level2. VFP crea una copia para la clase Level1 incluyendo el código del método. Como antes, las propiedades se evalúan primero. En este momento, CallMethod combina aun el código que ha sido copiado desde la clase de Level1.

Únicamente después de que todas las propiedades hayan sido evaluadas, Visual FoxPro agrega los códigos de los métodos a la clase Level2. Si crea una variable pública en ShowInfo.prg y guarda toRef en esa variable, obtiene una referencia a la clase. Cuando ejecuta CallMethod() en este objeto global  después de crear una instancia, obtiene además el código de la subclase.

Por tanto, recuerde: Las propiedades existen antes de que existan los métodos. Si no desea crear estas clases por si mismo, las puede descargar desde aquí:

http://www.foxpert.com/files/EggOrChicken.zip

2 de julio de 2016

Información con WSH

Podemos utilizar Microsoft ® Windows Script Host para mostrar información de nuestra PC, como por ejemplo: Dominio, Usuario, Nombre de PC, Unidades de red e Impresoras como lo muestra el siguiente ejemplo:

? "Información con WSH"
o = CREATEOBJECT('Wscript.Network')
? "Dominio: " + o.UserDomain
? "Usuario: " + o.UserName
? "Computadora: " + o.ComputerName
?
? "Unidades de red:"
oD = o.EnumNetworkDrives
FOR i = 0 TO oD.COUNT-1 STEP 2
  ? "Unidad: " + oD.ITEM(i) + " - Recurso: " + oD.ITEM(i+1)
ENDFOR
?
? "Impresoras:"
oP = o.EnumPrinterConnections
FOR i = 0 TO oP.COUNT-1 STEP 2
  ? "Puerto: " + oP.ITEM(i) +  " - Impresora: " + oP.ITEM(i+1)
ENDFOR
RELEASE oD
RELEASE oP
RELEASE o

Luis María Guayán

29 de junio de 2016

Utilizar FileMon

Artículo original: Using FileMon 
http://www.foxpert.com/knowlbits_200703_1.htm
Autor: Christof Wollenhaupt
Traducido por: Ana María Bisbé York


FileMon es una gran utilidad si necesita depurar problemas de rendimiento en una aplicación Visual FoxPro. Luego de lanzar FileMon.EXE como un administrador, cambie el filtro a "VFP9.EXE" o al nombre de su aplicación. Ahora, cuando ejecuta su aplicación, puede ver toda la actividad del disco en su aplicación.

Uno de los patrones principales a observar es una larga lista de accesos de lectura en un único archivo DBF. Estos son usualmente búsquedas de tabla que son el resultado de consultas no optimizadas o uso frecuente de agregados como calcular sumas o contar. Cuando verifica en una máquina local, raramente encontrará demora en estas operaciones ya que su disco duro es suficientemente rápido como para leer docenas de MB en apenas un segundo. En una red compartida, sin embargo, el ancho de banda es realmente crítico.

FileMon es muy bueno ya que dice qué es lo que está mal. Sin embargo, no ayuda a relacionarlo con la línea real de código. Para lograr esto, empleé marcadores en mi aplicación Visual FoxPro. Un marcador tiene la siguiente apariencia.

=FILE("MyClass.MyMethod.BeforeScan")

Es decir, yo verifico la existencia de un archivo no existente. En FileMon esta línea produce una salida similar a la siguiente:

11:14:08.182 vfp9.exe:2776 FASTIO_QUERY_OPEN
C:\DOCUMENTS AND SETTINGS\CHRISTOF\MY
DOCUMENTS\PROJECTS\lib\MyClass.MyMethod.BeforeScan
FILE NOT FOUND Attributes: Error

Ahora yo puedo buscar mis marcadores en FileMon. Puedo confiar que todas las actividades entre dos marcadores han sido causadas por el código que hay entre los dos marcadores. Al agregar y mover marcadores puedo identificar rápidamente las líneas que provocan mayor tráfico en una aplicación.