28 de septiembre de 2005

Y tu has explorado el ejemplo Solution.app?

En los foros de discusión de Microsoft se hacen decenas de preguntas con respecto a cosas que hacer para/con Visual FoxPro, muchas de ellas ya están explicadas en esta pequeña aplicación.

Desde los primeros días que empecé a manejar Visual FoxPro de manera profesional (es decir, ya me pagaban por eso) siempre me he metido mucho en la ayuda del producto, y una de las herramientas que me ha servido en demasía es el ejemplo Solutions, ahí ha estado desde hace años (por lo menos desde VFP5 que fue donde yo lo empecé a ver) y se ha ido actualizando con las distintas versiones que han salido.

Si no lo has visto es muy sencillo invocarlo, en la ventana de Comandos (Command Window) solo hay que ejecutar esta línea de código si quieres verlo en acción:

DO (HOME(2)+"Solution\Solution")

Lo que tendrás será un formulario que tiene decenas de ejemplos muy variados y muy útiles...


En ese formulario podrás ejecutar los ejemplos o ver su código (botones "Run Sample" y "See Code", respectivamente).

Si deseas ver el proyecto completo, puedes revisarlo con el siguiente comando:

MODIFY PROJECT (HOME(2)+"Solution\Solution")

Recomiendo ampliamente el revisarlos todos, ahí encontrará mucha información de trucos y técnicas para llevar a cabo varias tareas con Visual FoxPro.

Espero les sea de utilidad.

Espartaco Palma Martínez

24 de septiembre de 2005

Utilizar Windows Script Host para leer, escribir y eliminar claves del Registro

Artículo de la Base de Conocimientos de Microsoft que indica como utilizar Windows Script Host para leer, escribir y eliminar fácilmente claves del Registro de Windows desde Visual FoxPro.

El enlace al artículo de la MSKB (Microsoft Knowledge Base - Base de Conocimientos de Microsoft) es el siguiente:

Español (Traducción Automática):

-- Cómo utilizar el Windows Script Host para leer, escribir y eliminar claves del Registro --
https://support.microsoft.com/es-es/kb/244675

Inglés:

-- How to use the Windows Script Host to read, write, and delete registry keys --
https://support.microsoft.com/en-us/kb/244675

La información de este artículo se refiere a:

  • Microsoft Visual FoxPro 3.0
  • Microsoft Visual FoxPro 5.0
  • Microsoft Visual FoxPro 6.0 Professional Edition
  • Microsoft Visual FoxPro 7.0 Professional Edition
  • Microsoft Visual FoxPro 8.0 Professional Edition
  • Microsoft Visual FoxPro 9.0 Professional Edition

14 de septiembre de 2005

Nuevos valores para InputMask y Format en VFP 9.0

Ahora la propiedad InputMask permite los siguientes dos nuevos valores:
  • U = Permite solo caracteres alfabéticos y los convierte a mayúsculas [A..Z]
  • W = Permite solo caracteres alfabéticos y los convierte a minúsculas [a..z]
La propiedad Format permite el siguente valor en mas controles:
  • Z = Muestra el valor como "blanco" si este es "0" (cero), excepto cuando el control tiene el foco.
Los tipos de datos Date y DateTime también están soportados cuando la propiedad Format tiene el valor "Z". Cuando la fecha ó fecha y hora son vacias, no se muestran los delimitadores " / / " ó " / / : : " cuando el control no tiene el foco.

Un ejemplo para Visual FoxPro 9.0 de estas propiedades se pueden observar en el siguiente formulario:
PUBLIC goForm
goForm = CREATEOBJECT("MiForm")
goForm.SHOW(1)
RETURN

DEFINE CLASS MiForm AS FORM
AUTOCENTER = .T.
  HEIGHT = 250
  WIDTH = 324
  CAPTION = "VFP 9.0 - Nuevas configuraciones"
  NAME = "MiForm"
  ADD OBJECT Text1 AS TEXTBOX WITH ;
    HEIGHT = 23, ;
    LEFT = 192, ;
    TOP = 36, ;
    WIDTH = 120, ;
    MAXLENGTH = 10, ;
    INPUTMASK = REPLICATE("U",10), ;
    NAME = "Text1"
  ADD OBJECT Text2 AS TEXTBOX WITH ;
    HEIGHT = 23, ;
    LEFT = 192, ;
    TOP = 72, ;
    WIDTH = 120, ;
    MAXLENGTH = 10, ;
    INPUTMASK = REPLICATE("W",10), ;
    NAME = "Text2"
  ADD OBJECT Text3 AS TEXTBOX WITH ;
    HEIGHT = 23, ;
    LEFT = 192, ;
    TOP = 132, ;
    WIDTH = 120, ;
    VALUE = 0, ;
    FORMAT = "Z", ;
    NAME = "Text3"
  ADD OBJECT Text4 AS TEXTBOX WITH ;
    HEIGHT = 23, ;
    LEFT = 192, ;
    TOP = 168, ;
    WIDTH = 120, ;
    VALUE = {}, ;
    FORMAT = "Z", ;
    NAME = "Text4"
  ADD OBJECT Label1 AS LABEL WITH ;
    AUTOSIZE = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "Mayusculas solamente", ;
    HEIGHT = 17, ;
    LEFT = 12, ;
    TOP = 40, ;
    WIDTH = 129, ;
    NAME = "Label1"
  ADD OBJECT Label2 AS LABEL WITH ;
    AUTOSIZE = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "Minusculas solamente", ;
    HEIGHT = 17, ;
    LEFT = 12, ;
    TOP = 76, ;
    WIDTH = 127, ;
    NAME = "Label2"
  ADD OBJECT Label3 AS LABEL WITH ;
    AUTOSIZE = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "Blanco si es '0'", ;
    HEIGHT = 17, ;
    LEFT = 12, ;
    TOP = 136, ;
    WIDTH = 88, ;
    NAME = "Label3"
  ADD OBJECT Label4 AS LABEL WITH ;
    AUTOSIZE = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "Blanco si es fecha vacia", ;
    HEIGHT = 17, ;
    LEFT = 12, ;
    TOP = 172, ;
    WIDTH = 134, ;
    NAME = "Label4"
  ADD OBJECT Label5 AS LABEL WITH ;
    AUTOSIZE = .T., ;
    FONTBOLD = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "Propiedad InputMask = 'U' y 'W'", ;
    HEIGHT = 17, ;
    LEFT = 78, ;
    TOP = 12, ;
    WIDTH = 167, ;
    NAME = "Label5"
  ADD OBJECT Label6 AS LABEL WITH ;
    AUTOSIZE = .T., ;
    FONTBOLD = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "Propiedad Format = 'Z'", ;
    HEIGHT = 17, ;
    LEFT = 100, ;
    TOP = 108, ;
    WIDTH = 123, ;
    NAME = "Label6"
  ADD OBJECT Label7 AS LABEL WITH ;
    AUTOSIZE = .F., ;
    WORDWRAP = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "En ambos casos se muestra en blanco " + ;
    "cuando el control no tiene el foco", ;
    HEIGHT = 36, ;
    LEFT = 24, ;
    TOP = 204, ;
    WIDTH = 276, ;
    FORECOLOR = RGB(0,0,255), ;
    NAME = "Label7"
ENDDEFINE
Recordar que la diferencia entre las propiedades InputMask y Format es que con la propiedad Format se especifica un comportamiento para todo el campo de entrada, y con la propiedad InputMask se especifica que cada caracter de la máscara corresponde a cada caracter ingresado en el campo de entrada.

Luis María Guayán

12 de septiembre de 2005

Alcance en Visual FoxPro - Parte 2

Artículo original: Scoping in Visual FoxPro - Part 2
http://weblogs.foxite.com/andykramek/archive/2005/05/13/446.aspx
Autor: Andy Kramek
Traducido por: Ana María Bisbé York

... continuación de "Alcance en Visual FoxPro - Parte 1"

Como he prometido, hoy continuaré nuestra revisión de temas relativos al alcance en Visual FoxPro con una mirada a cómo el alcance afecta las propiedades (y métodos) de los objetos. Creo que será más fácil pensar en las propiedades como "variables que están limitadas por los objetos". En otras palabras, mientras existan los objetos, existirán sus propiedades. Tan pronto como sea liberado el objeto, sus propiedades (y sus valores) se librarán también. En este sentido, al menos, cabe la analogía con las variables de memoria y su alcance (público, privado y local). Sin embargo, a pesar de la primera apariencia, no se crean por igual todas las propiedades.

VFP es algo inusual entre los lenguajes orientados a objeto en que todas sus propiedades, eventos y métodos (PEMs) de sus clases se crean de forma predeterminada como PUBLIC. Es importante entender que en el contexto de un objeto, la palabra "public" se refiere a la visibilidad de una propiedad (o método) de otro objeto u otros objetos. De hecho, hablando adecuadamente, el conjunto de PEMs que expone un objeto al mundo exterior es su "Interfaz pública". En muchos lenguajes orientados a objeto las propiedades, métodos y eventos nativos no se exponen en la interfaz pública y es el desarrollador quien se encarga de definir los que desea exponer.

VFP hace justamente lo contrario.  A menos que el desarrollador decida otra cosa, todas las PEMs nativas o de usuario, se exponen como parte de la interfaz pública de un objeto y tienen alcance público.

Esto, por supuesto, es de modo general, algo muy bueno, en una herramienta de desarrollo de aplicaciones como Visual FoxPro. Imagine qué tortura sería si cada vez que desee referirse a una propiedad de un objeto como un textbox (por ejemplo: Value, Background Color, Height, Width, Font) desde cualquier otro objeto, antes, tener que definir esta propiedad como "PUBLIC" o escribir un código en algún método especial del objeto en cuestión. Esto es, sin embargo, como ocurre en muchos otros lenguajes orientados a objeto. Vea las propiedades definidas en C# (o en una interfaz COM) y va a ver que existen dos métodos relacionados con ellas - un método "PUT" para asignar un valor y un método "GET" para devolver el valor. ¡ Contraste esto con la facilidad de crear y trabajar con propiedades en VFP !

Sin embargo, hay momentos en los que no deseamos que una propiedad (o método) específicos sean visibles a los objetos externos. ¿Porqué? Puede ser porque la propiedad es esencial en trabajos internos del objeto y no desea cambiarlos salvo  condiciones específicas y controladas o debido a que ese método debe ser llamado solamente como parte de, o en conjunto, con alguna otra operación. Por esta razón VFP implementa dos palabras reservadas adicionales que se pueden emplear para definir el alcance de PEMs.
  • PROTECTED - Las PEMs con alcance PROTECTED (protegidos) son solamente accesibles por los objetos que son instanciados desde una clase que define la PEM o desde una clase que herede desde la clase que lo define. Debido a que Visual FoxPro no implementa herencia múltiple, podemos definir brevemente: "son visibles solamente por la clase que la origina y sus subclases"
  • HIDDEN - Las PEMs con alcance HIDDEN (ocultos) son accesibles solamente por el objeto que los ha instanciado, directamente de la clase que define la PEMs. En otras palabras ellos son: "visibles solamente dentro de la clase original"
Pudiera preguntarse, en este punto, qué ocurre si trata de acceder a una PEM que esté protegida u oculta. La respuesta es sencilla, obtiene un error. Pegue el siguiente código en un PRG y ejecútelo:
oTest = CREATEOBJECT('xObj')
CLEAR
?
? "La siguiente línea va a causar un error - oprima Ignorar"
? "Accede a la propiedad directamente para ver el valor: " + oTest.cProperty
?
? "Llama al método Get y lee el valor: " + oTest.GetProperty()

DEFINE CLASS xObj AS Session
  cProperty = 'Esta propiedad no es visible'' 
  PROTECTED cProperty
  
  FUNCTION GetProperty()
    RETURN This.cProperty
  ENDFUNC
ENDDEFINE
Como puede ver, al declarar la propiedad protegida obtenemos un error " No existe la propiedad <nombre>" al tratar de acceder directamente desde fuera. Sin embargo, cuando llamamos al método GetProperty() - que es público - no hay problemas. Este método está definido en la clase que define la propiedad, y entonces puede ver la propiedad, y devolver su valor incluso si quien lo llama no puede ver la propiedad directamente. Por supuesto, el método pudo hacer mucho más que sencillamente devolver un valor. Los usos frecuentes para una propiedad protegida, con un método Get() que la exponga, incluyen cálculo secuencial, o valores acumulados (ejecutando totales). Cada vez que se llama al método, genera el siguiente número (o agrega algún valor) a la propiedad y devuelve el valor nuevo. El punto es que la UNICA vía para cambiar el valor desde fuera del objeto es llamar al método y que nos permita verificar que el valor se cambie sólo cuando se suponga que cambie. (contraste esto con hacer ThisForm.Text1.Value = "xxxxx").


9 de septiembre de 2005

Alcance en Visual FoxPro - Parte 1

Artículo original: Scoping in Visual FoxPro - Part 1
http://weblogs.foxite.com/andykramek/archive/2005/05/04/421.aspx
Autor: Andy Kramek
Traducido por: Ana María Bisbé York

Lamento la demora que ha habido en esta publicación. Las cosas han estado un poco agitadas en los últimos 10 días. En cualquier caso, he aquí la siguiente serie. Esta vez voy a referirme al alcance de las variables y la semana que viene voy a continuar con las Propiedades y métodos. Espero que lo disfruten.

Alcance en Visual FoxPro - Parte 1

Uno de los aspectos que causa confusión es el alcance de propiedades, métodos y variables en Visual FoxPro. De hecho, es realmente muy sencillo; pero puede ser confuso debido a las diferencias en la sintaxis y su significado. Comencemos, esta semana, con las variables y veremos las propiedades y métodos la semana que viene.

Alcance de las variables

El alcance de las variables define cuánto tiempo existirán y qué programas, métodos, procedimientos o Funciones Definidas por el usuario (UDFs) tienen acceso a ellas. Para ver el tema del alcance veremos el término "procedimiento" que incluye programas, métodos, procedimientos y UDFs. Teniendo esto en mente, existen tres niveles de alcance.

Variables públicas (Public)

Se conocen también como Globales - de hecho se recomienda emplear g como identificador de alcance para variables públicas (la letra "p" se reserva para variables privadas "private"). Una variable pública está disponible en una aplicación entera independientemente de dónde o cuándo es creada. Existe, hasta que sea liberada con RELEASE o que la aplicación (o el VFP) se cierren. Esto significa que cualquier programa puede leer, o cambiar el valor de la variable en cualquier momento. Mientras existen situaciones muy específicas en las que la variable pública es la única solución, como regla general su uso en código de programas no es recomendado - son demasiado vulnerables a cambios inesperados, como para ser confiables.

Habiendo dicho esto, es importante comprender que todas las variables creadas en la ventana de comandos tienen alcance público - y esto es lo que produce el grito de "trabaja en la ventana de comandos; pero no en el EXE". Aunque hay una excepción. En todas las otras circunstancias una variable pública debe ser declarada explícitamente antes que le sea asignado un valor y si no lo hacemos ocurre un error. Por esta razón vemos frecuentemente PUBLIC precedido por un comando RELEASE (vea el código abajo). Al ejecutar un comando PUBLIC se crea(n) la(s) variable(s) y se inicializa(n) en falso .F.
*** Declara un par de variables públicas
RELEASE gnPubNum, gcPubStr
PUBLIC gnPubNum, gcPubStr
*** Hasta que le sea asignado un valor específico
*** las variables públicas tienen valor .F.
gnPubNum = 100
gcPubStr = "cadena de ejemplo"

Variables privadas (Private)

Una variable privada está disponible para el procedimiento que la creó y para cualquier procedimiento que sea llamado por el que la crea, o llamado en la cadena de procedimientos inicializada por el que la crea. Cuando finaliza este procedimiento, la variable se libera automáticamente. Si no ha sido especificado otro alcance, cuando se asigna un valor a una variable, Visual FoxPro la implementa como variable privada. Esto significa que todo lo que hace falta para crear una variable privada es, asignarle valor:
*** Crea las siguientes variables como privadas
pnPriNum = 100
pcPriStr = "Cadena Private"
Aunque,  hay que tener cuidado en una cosa. Si el nombre de la variable ya existe con un alcance determinado (por ejemplo ha sido declarada como Public o Private en un procedimiento padre, las asignaciones mostradas antes no van a crear variables nuevas, sencillamente van a cambiar el valor de la variable existente con ese nombre. No es siempre lo que buscamos y puede ser muy difícil encontrar el lugar que ocasiona un problema. Considere el código siguiente:
*** Ejecuta el procedimiento hijo 3 veces
FOR lnCnt = 1 TO 3
  CallChild(lnCnt * 4
)NEXT
*** Definición del procedimiento hijo
CallChild(tnCounter)
FOR lnCnt = 1 TO tncounter
  ? "Número del lazo: " + TRANSFORM(lnCnt)
NEXT
Pregúntese ahora, ¿cuántas veces se ejecutará el procedimiento hijo? La respuesta es, por supuesto, una vez ! El mismo nombre de contador de ciclo (lnCnt) se emplea en ambos procedimientos (padre e hijo), con el resultado que después de la primera llamada al procedimiento hijo el valor será 4 (es decir lnCnt = 1* 4) y se rompe el ciclo del programa padre. Este es un ejemplo bien obvio de cuán mal pueden ir las cosas con las variables privadas - suponga que en lugar de un par de líneas, el segundo empleo de lnCnt es un archivo de programa diferente ejecutado tres o cuatro niveles más abajo en la cadena de llamadas. Depurar lo que está mal puede ser un gran dolor de cabeza.

Afortunadamente, el uso del comando PRIVATE controla este tema por nosotros. Sin embargo, a diferencia del comando PUBLIC no crea automáticamente una variable nueva. En su lugar, dice a Visual FoxPro que cuando (y si) un nuevo valor es asignado a un nombre de variable específico, debe ignorar cualquier variable existente y crear una nueva, de la que es propietaria el procedimiento actual. Si se re-utiliza un nombre de variable existente (como hice en el ejemplo anterior); pero declarando PRIVATE antes de asignar un valor y luego mirar en el depurador, verá que VFP conserva la variable original; pero ahora está oculta "hidden" y se ha creado una nueva.
Nota: La sentencia PARAMETERS es equivalente a PRIVATE. Las variables definidas empleando este comando son creadas como si hubieran sido declaradas explícitamente como privadas con el mismo nombre que tienen alcance oculto hidden.
Variables locales (Local)


2 de septiembre de 2005

Autocompletar en Cuadros de Textos de VFP 9.0

Introducción

Una nueva característica de Visual FoxPro 9.0 es que el control TextBox (Cuadro de Texto) tiene la funcionalidad de Autocompletar, que nos sugiere valores de una tabla, a medida que se introducen caracteres en él. Si se selecciona un valor de la lista desplegada, el cuadro de texto se completa y se actualiza la tabla que contiene los datos almacenados.

Esta nueva característica ya fue tratada en un artículo de Jorge Mota (Guatemala) publicado en PortalFox: "Usando Autocompletar en TextBoxs de VFP 9"

En este nuevo artículo veremos como podemos hacer para que Autocompletar nos sugiera valores de una tabla de nuestra aplicación.

Las nuevas propiedades

Visual FoxPro 9.0 incorporó tres nuevas propiedades al control TextBox para esta funcionalidad: AutoComplete, AutoCompSource y AutoCompTable.

Si la propiedad AutoComplete se configura con un valor distinto de cero (el valor cero indica que no está habilitado Autocompletar), a medida que se introducen caracteres, se sugieren valores con la exhibición de una lista desplegada. Esta lista muestra los valores que concuerden con los valores previamente almacenados. Según el valor que se configure esta propiedad, la lista se ordenará "Alfabéticamente", "El mas frecuentemente usado", "El mas recientemente usado" o "Personalizado", que se ordena descendentemente por el "peso" que se le da al valor mediante un algorítmo realizado por el usuario que modifique el campo Weight (Peso) de la tabla AutoComp. Los valores permitidos para esta propiedad son 0, 1, 2, 3 y 4.

La propiedad AutoCompSource almacena el nombre clave que se utilizará para buscar en la tabla AutoComp. Si se omite esta propiedad, el nombre del control será la clave de búsqueda en la tabla. Se puede lograr que varios controles del tipo TextBox, utilicen la misma lista, configurando esta propiedad con un valor igual en todos los controles.

La propiedad AutoCompTable especifica el nombre y la ruta de la tabla que almacena los datos usados por un control TextBox para sugerir los valores. El valor por omisión de esta propiedad es (HOME(7) + "autocomp.dbf"). Si la tabla no existe, VFP la creará automáticamente.
Nuestro caso

Para nuestro caso de que Autocompletar nos sugiera los valores de una tabla de nuestra aplicación, haremos hincapié en la propiedad AutoCompTable y la configuraremos con la ruta y nombre de una tabla que crearemos y poblaremos con los datos de nuestra tabla de acuerdo a la estructura y descripción de cada campo de la tabla AutoComp que es la siguiente:

CampoTipoDescripción
Source Caracter(20) Nombre de la fuente del control TextBox.
Data Caracter(254) Dato a mostrar.
Count Entero Número de veces que el dato fue seleccionado.
Weight Entero Especifica el valor a usar cuando AutoComplete esta configurado en 4.
Created DateTime Fecha y hora de creación del dato.
Updated DateTime Fecha y hora de la última modificación del dato.
User Memo Especifica información de usuario.

La tabla AutoComp esta indexada con la siguiente clave:
INDEX ON ;
  UPPER(SOURCE + LEFT(DATA,30)) + PADL(COUNT,8) ;
  TAG DATA FOR NOT DELETED()

El ejemplo

Para nuestro ejemplo tomaremos la tabla "Customers" de la base de datos "Northwind" que viene junto a los ejemplos de Visual FoxPro 9.0. Los campos que seleccionaremos e insertaremos en la tabla son "CompanyName", "ContactName", "City" y "Country".

Primeramente invocamos al método "CrearTablaAutoComp()" que crea la tabla "MiAutoComp.dbf" en el directorio de archivos temporales de Windows y la indexa.

Luego por cada control TextBox en los cuales queremos habilitar la funcionalidad de autocompletar, invocamos al método "InsertarReg()" que inserta en la tabla "lcDbfAuto", los datos de la tabla "lcDbfOrigen" y del nombre de campo y nombre de clave que pasamos como parámetros, mediante un "INSERT INTO ... FROM SELECT ..."; y configuramos las propiedades "AutoComplete", "AutoCompSource" y "AutoCompTable".

El siguiente código genera un formulario con cuatro cuadros de textos con las propiedades configuradas para que Autocompletar nos sugiera los valores de nuestra tabla "MiAutoComp".

PUBLIC goMiForm
goMiForm = CREATEOBJECT("MiForm")
goMiForm.SHOW(1)
RETURN

DEFINE CLASS MiForm AS FORM
  AUTOCENTER = .T.
  CAPTION = "Ejemplo de Autocompletar en TextBox"
  NAME = "MiForm"
  ADD OBJECT txtCompania AS TEXTBOX WITH ;
    HEIGHT = 24, ;
    LEFT = 84, ;
    TOP = 24, ;
    WIDTH = 264, ;
    NAME = "txtCompania"
  ADD OBJECT txtContacto AS TEXTBOX WITH ;
    HEIGHT = 24, ;
    LEFT = 84, ;
    TOP = 72, ;
    WIDTH = 264, ;
    NAME = "txtContacto"
  ADD OBJECT txtCiudad AS TEXTBOX WITH ;
    HEIGHT = 24, ;
    LEFT = 84, ;
    TOP = 120, ;
    WIDTH = 264, ;
    NAME = "txtCiudad"
  ADD OBJECT txtPais AS TEXTBOX WITH ;
    HEIGHT = 24, ;
    LEFT = 84, ;
    TOP = 168, ;
    WIDTH = 264, ;
    NAME = "txtPais"
  ADD OBJECT lblCompania AS LABEL WITH ;
    AUTOSIZE = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "Compañia", ;
    LEFT = 12, ;
    TOP = 28, ;
    NAME = "lblCompania"
  ADD OBJECT lblContacto AS LABEL WITH ;
    AUTOSIZE = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "Contacto", ;
    LEFT = 12, ;
    TOP = 76, ;
    NAME = "lblContacto"
  ADD OBJECT lblCiudad AS LABEL WITH ;
    AUTOSIZE = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "Ciudad", ;
    LEFT = 12, ;
    TOP = 124, ;
    NAME = "lblCiudad"
  ADD OBJECT lblPais AS LABEL WITH ;
    AUTOSIZE = .T., ;
    BACKSTYLE = 0, ;
    CAPTION = "País", ;
    LEFT = 12, ;
    TOP = 172, ;
    NAME = "lblPais"

  PROCEDURE INIT
    LOCAL lcDbfAuto, lcDbfOrigen
    *-- Ruta y nombre de la tabla AutoCompletar
    lcDbfAuto = ADDBS(SYS(2023)) + "MiAutoComp.dbf"
    *-- Creo la tabla AutoCompompletar
    THISFORM.CrearTablaAutoComp(lcDbfAuto)

    *-- Ruta y nombre de la tabla Origen
    lcDbfOrigen = ADDBS(HOME(2))+ "NorthWind\Customers.dbf"

    *-- Inserto los registros para cada campo
    *-- y configuro las propiedades de cada TextBox
    THISFORM.InsertarReg(lcDbfOrigen,lcDbfAuto,"AC_COMPANIA","CompanyName")
    THISFORM.txtCompania.AUTOCOMPLETE = 1
    THISFORM.txtCompania.AUTOCOMPSOURCE = "AC_COMPANIA"
    THISFORM.txtCompania.AUTOCOMPTABLE = lcDbfAuto

    THISFORM.InsertarReg(lcDbfOrigen,lcDbfAuto,"AC_CONTACTO","ContactName")
    THISFORM.txtContacto.AUTOCOMPLETE = 1
    THISFORM.txtContacto.AUTOCOMPSOURCE = "AC_CONTACTO"
    THISFORM.txtContacto.AUTOCOMPTABLE = lcDbfAuto

    THISFORM.InsertarReg(lcDbfOrigen,lcDbfAuto,"AC_CIUDAD","City")
    THISFORM.txtCiudad.AUTOCOMPLETE = 1
    THISFORM.txtCiudad.AUTOCOMPSOURCE = "AC_CIUDAD"
    THISFORM.txtCiudad.AUTOCOMPTABLE = lcDbfAuto

    THISFORM.InsertarReg(lcDbfOrigen, lcDbfAuto,"AC_PAIS","Country")
    THISFORM.txtPais.AUTOCOMPLETE = 1
    THISFORM.txtPais.AUTOCOMPSOURCE = "AC_PAIS"
    THISFORM.txtPais.AUTOCOMPTABLE = lcDbfAuto
  ENDPROC

  PROCEDURE CrearTablaAutoComp(tcDbfAuto)
    *-- Crea mi tabla Autocompletar
    SET SAFETY OFF
    CREATE TABLE (tcDbfAuto) FREE ;
      (SOURCE C(20), DATA C(254), COUNT I, Weight I, ;
      Created T, UPDATED T, USER M)
    *-- Indexo la tabla
    INDEX ON UPPER(SOURCE + LEFT(DATA,30)) + PADL(COUNT,8) ;
      TAG DATA FOR NOT DELETED()
    *-- Cierro tabla
    USE IN SELECT(JUSTSTEM(tcDbfAuto))
  ENDPROC

  PROCEDURE InsertarReg(tcDbfOrigen, tcDbfAuto, tcSource, tcData)
    *-- Inserto los registros a la tabla Autocompletar
    INSERT INTO (tcDbfAuto) ;
      SELECT DISTINCT UPPER(tcSource) AS SOURCE, ;
      EVALUATE(tcData) AS DATA, 0 AS COUNT, 0 AS Weight, ;
      DATETIME() AS Created, DATETIME() AS UPDATED, .F. AS USER ;
      FROM (tcDbfOrigen)
    *-- Cierro tablas
    USE IN SELECT(JUSTSTEM(tcDbfAuto))
    USE IN SELECT(JUSTSTEM(tcDbfOrigen))
  ENDPROC
ENDDEFINE

Si ejecutamos el código e introducimos valores en los cuadros de textos, se mostraran las listas con los valores sugeridos como se observa en la siguiente imagen:



El número de items mostrados en la lista desplegable por defecto es 15. Se puede configurar este número con la siguiente función:

SYS(2910,nValor) && nValor = [5..200]

Conclusiones

Este ejemplo es solo para mostrar como podemos sugerir valores en un cuadro de texto en Visual FoxPro 9.0. Queda en nosotros saber si conviene o no utilizar esta técnica con el control TextBox o elegir otro tipo de control como un ComboBox o ListBox.

Hasta la próxima.

Luis María Guayán