Controlar a un objeto desde otro objeto

Una de las ventajas de la programación orientada a objetos es que resulta fácil enlazar o relacionar a un objeto con otro objeto. Lo único que se necesita es que el objeto controlador tenga una referencia al objeto controlado. Si tiene esa referencia entonces podrá acceder a todas las propiedades y a todos los métodos del objeto controlado.

¿Y por qué haríamos algo así?

Una de las utilidades es poder modificar la apariencia o el comportamiento de un formulario o de cualquiera de sus controles sin cambiar casi nada del código fuente ni del formulario ni de los controles contenidos en ese formulario. En general, además de agregar el control controlador al formulario lo único que deberemos hacer es llamar a uno de los métodos de ese control en el método INIT() de nuestro formulario. Y ya está.

Ejemplo. Mostrando mensajes cuando el usuario hace clic derecho en un «Header»

Primero. Creamos la clase:

CREATE CLASS MOSTRAR_TOTALES OF CONTROLES AS CUSTOM

No necesariamente nuestra nueva clase debe estar basada en CUSTOM, puede estar basada en cualquier otra clase, nativa o no nativa del Visual FoxPro, por ejemplo podría ser: COMBOBOX, COMMANDBUTTON, LABEL, LISTBOX, TEXTBOX, etc. Y la biblioteca de clases puede tener cualquier nombre, aquí se usó CONTROLES pero tú puedes darle cualquier otro nombre que prefieras.

Segundo. Le agregamos algunas propiedades a la nueva clase

Como en este caso queremos que nuestra nueva clase modifique el comportamiento de una grilla entonces le agregamos las propiedades:

  • cNombreGrilla
  • oGrilla

En la propiedad cNombreGrilla escribiremos el nombre de la grilla que será controlada. Por ejemplo: «ThisForm.Grid1»

En la propiedad oGrilla tendremos una referencia a la grilla que será controlada.

Tercero. Le agregamos algunos métodos a la nueva clase

En este ejemplo los métodos son:

  • BOTON_DERECHO()
  • CONFIGURAR()

El método BOTON_DERECHO() se enlazará mediante la función BINDEVENT() con el método RightClick() de cada «Header» de la grilla.

El método CONFIGURAR() será llamado desde el método INIT() del formulario y realizará todas las tareas de inicialización necesarias.

Listado 1. El método CONFIGURAR()

LOCAL loColumna, lcHeader, loHeader
  
  IF !EMPTY(This.cNombreGrilla) THEN
    This.oGrilla = EVALUATE(This.cNombreGrilla)
  ELSE
    This.oGrilla = .NULL.
    RETURN
  ENDIF
  
  FOR EACH loColumna IN This.oGrilla.Columns
    lcHeader = "This.oGrilla." + loColumna.Name + ".Header1"
    loHeader = EVALUATE(lcHeader)
    =BINDEVENT(loHeader, "RightClick", This, "BOTON_DERECHO")
  ENDFOR
  
RETURN
*
*

Este método primero guarda en la propiedad oGrilla una referencia a la grilla que será manipulada. Eso nos permitirá acceder a todas las propiedades y a todos los métodos de esa grilla. Luego enlaza el método RigthClick() de cada «Header» de la grilla con el método BOTON_DERECHO() de nuestra clase controladora. ¿Para qué? para que así podremos escribir código en el método BOTON_DERECHO() y ese código se ejecutará cuando el usuario haga clic derecho sobre cualquiera de los «Header».

O sea que, sin tocar para nada a la grilla, podremos manipularla según nuestros deseos.

Listado 2. El método BOTON_DERECHO()

LOCAL ARRAY laHeader[1, 1]
LOCAL loHeaderActual, loColumnaActual, lcControlSourceActual, lcVarTypeActual
LOCAL lnRecNo, lnSumaColumna
  
  =AEVENTS(laHeader, 0)
  
  loHeaderActual        = laHeader[1]
  
  loColumnaActual       = loHeaderActual.Parent
  
  lcControlSourceActual = loColumnaActual.ControlSource
  
  lcVarTypeActual       = VARTYPE(EVALUATE(lcControlSourceActual))
  
  DO CASE
    CASE lcVarTypeActual == "C"
      =MESSAGEBOX("Hmmmm...no puedo totalizar una columna de tipo carácter")
    CASE lcVarTypeActual == "D"
      =MESSAGEBOX("No sé como totalizar a las fechas")
    CASE lcVarTypeActual == "L"
      =MESSAGEBOX("Vaya...hay valores lógicos en esta columna")
    CASE lcVarTypeActual == "N" .OR. lcVarTypeActual == "Y"
      lnRecNo = RECNO()
      SUM EVALUATE(lcControlSourceActual) TO lnSumaColumna
      IF lnRecNo <= RECCOUNT() THEN
        GO lnRecNo
      ENDIF
      =MESSAGEBOX("La suma de la columna " + loColumnaActual.NAME + " es: " + TRANSFORM(lnSumaColumna))
    OTHERWISE
      =MESSAGEBOX("Aún no sé como procesar este tipo de variables")
  ENDCASE
  
RETURN
*
*

En este método lo primero que hacemos es llamar a la función AEVENTS(). Esa función crea un array que contiene el objeto que desencadenó al último evento. ¿Por qué se la usa? porque nosotros sabemos que el usuario hizo clic derecho sobre un «Header», por eso se llamó al método BOTON_DERECHO() pero ¿en cual de los «Header» hizo clic derecho? ¿el de la columna 1, el de la columna 2, el de la columna 3, …?

Ahora podremos saberlo porque al tener una referencia al «header» que desencadenó el último evento podremos averiguar en cual columna está ese «header» simplemente accediendo a su parent.

Y si tenemos una referencia a la columna correcta también tenemos acceso a todas las propiedades y a todos los métodos de esa columna. Por ejemplo podemos saber el nombre del campo guardado en la propiedad ControlSource.

Y si sabemos cual es el campo que está guardado en la propiedad ControlSource entonces averiguar cual es el tipo de datos (carácter, fecha, lógico, numérico, etc.) de ese campo ya es muy fácil

Como puedes ver, controlar a un objeto (una grilla en este ejemplo) desde otro objeto no es complicado. Solamente debemos tener una propiedad (oGrilla en este ejemplo) que haga referencia al objeto que será controlado.

En uno de los formularios que puedes descargar más abajo se agregaron una grilla y un controlador de esa grilla.

Captura 1. En el formulario hay una grilla y un objeto controlador de esa grilla

En un formulario puede haber varias grillas, ¿cómo sabe el objeto controlador a cual de esas grillas debe controlar? pues ese dato lo especificamos en la propiedad cNombreGrilla.

Captura 2. En una propiedad de nuestro objeto controlador debemos escribir el nombre del objeto que será controlado.

El resultado de llamar a ese formulario, y hacer clic derecho sobre uno de los «Header» lo podemos ver a continuación:

Captura 3. El resultado obtenido al ejecutar el formulario que tiene una grilla y un objeto controlador de esa grilla

Lo súper interesante de la Captura 3. es que nada de nada se tocó en la grilla. Se modificó su comportamiento sin agregarle nada al código fuente de sus métodos ni se cambió el valor de alguna de sus propiedades.

¿Te das cuenta de lo poderoso que eso es?

Significa que puedes cambiar la apariencia o la funcionalidad de cualquiera de tus formularios sin necesidad de cambiar algo en ellos, simplemente le agregas un objeto controlador, en el método INIT() de tu formulario llamas a un método de ese objeto controlador y listo, eso es todo.

Y si no te gusta el resultado obtenido, eliminas al objeto controlador que pusiste en tu formulario y quedó todo exactamente como estaba antes.

La cantidad de posibilidades es inmensa, ahora queda a tu cargo obtener buen provecho de esta técnica.

Conclusión

Poder controlar a un objeto (o a muchos objetos) sin tocarlo, sin cambiar nada en sus métodos ni en sus propiedades nos permite muchísimas posibilidades.

Por ejemplo, podríamos agregar funcionalidades personalizadas para cada usuario. En una tabla guardamos sus preferencias, nuestro objeto controlador lee y establece esas preferencias, y así cada usuario tiene lo que desea tener.

O podríamos agregar un módulo que solamente estará disponible para los clientes que nos paguen por ese módulo. Si pagan, se lo habilitamos, si no pagan no se lo habilitamos. El que no paga no verá cambios, todo continuará igual para él. En cambio el que sí pagó tendrá algo nuevo a su disposición. Y si debe pagar en cuotas y está atrasado con sus cuotas, le podremos fácilmente deshabilitar u ocultar ese módulo. Nuestro objeto controlador se encargará de esas tareas.

Descargas

Puedes descargar un archivo .ZIP que contiene dos archivos .PRG, dos formularios, y una biblioteca de clases desde:

Hay 2 archivos .PRG porque hay 2 ejemplos. Para probarlos, en la ventana de comandos del Visual FoxPro escribe:

DO EJEMPLO1

DO EJEMPLO2

Artículos relacionados

Tratamiento fácil de las imágenes

Normalmente se usan aplicaciones de terceros para manipular las imágenes, siendo que muchas tareas se pueden realizar directamente desde el Visual FoxPro lo cual nos haría ahorrar mucho tiempo. Por ejemplo, podríamos:

  • Capturar la pantalla
  • Capturar el clipboard (portapapeles)
  • Cambiar el formato de una imagen
  • Rotar una imagen
  • Poner un texto sobre una imagen
  • Hacer transparente una imagen
  • Cambiar las dimensiones de una imagen
  • Insertar una imagen sobre otra imagen

Capturar la pantalla. Se la podría enviar por e-mail o por Whatsapp cuando ocurre un error.

Capturar el clipboard (portapapeles). Si hay una imagen gráfica en el «clipboard», se puede grabar esa imagen en un archivo.

Cambiar el formato de una imagen. Para cambiar entre BMP, GIF, JPG, PNG, TIFF. Muy útil cuando tenemos una imagen que nos gusta pero que no está en el formato que necesitamos.

Rotar una imagen. Para que se la vea desde otra perspectiva.

Poner un texto sobre una imagen. Súper útil para ponerle comentarios, descripciones, y «marcas de agua». Por ejemplo sobre la imagen de un producto se escriben su código, su nombre, y su precio de venta. No importaría que el precio de venta cambie porque siempre se mostrará el precio de venta actual. También se puede proteger una imagen escribiendo un texto sobre ella de tal manera que si alguien descarga la imagen la descargará con el texto que escribimos. Borrar ese texto le puede hacer demorar mucho tiempo al intruso.

Hacer transparente una imagen. Es muy frecuente que necesitemos imágenes con fondo transparente.

Cambiar las dimensiones de una imagen. Muy útil para que todas nuestras imágenes tengan el mismo alto y el mismo ancho. Eso nos facilitará las tareas de escribir texto sobre ellas y de ponerles «marcas de agua» porque no necesitaremos estar verificando una por una las dimensiones de las imágenes. También sirve para ahorrar espacio dentro de la Base de Datos o dentro del disco duro porque si en nuestro formulario mostramos una imagen con una resolución de 320 x 240 (por ejemplo) es mucho desperdicio de espacio que esa imagen esté grabada con una resolución de 1920 * 1440. Además que cuanto más liviana es una imagen más rápido se carga en la memoria lo cual puede ser mucho ahorro de tiempo cuando hay una gran cantidad de imágenes.

Insertar una imagen sobre otra imagen. Por ejemplo para ponerle una «marca de agua» que no sea solamente texto. Cualquier imagen se podría colocar sobre otra imagen. También para hacer «collages».

La biblioteca de clases _GDIPLUS.VCX

Hay una biblioteca de clases gratuita llamada _GDIPLUS.VCX la cual nos permite realizar fácilmente todas las tareas mostradas arriba y muchas más. Para no hacer este artículo demasiado largo mostraré solamente a ellas pero si te despertó la curiosidad entonces podrías investigar a _GDIPLUS.VCX, hay mucha documentación en Internet.

Ejemplos

Captura 1. Toda la pantalla de la computadora ha sido capturada. Muy útil para enviarla por e-mail o por Whatsapp cuando el usuario detecta un error.

Captura 2. Si en el «clipboard» se había copiado una imagen, esa imagen es grabada en un archivo gráfico.
Captura 3. En este caso una imagen que originalmente estaba grabada con formato JPG se grabó con formato PNG, que es la mostrada aquí.
Captura 4. Se rotó 90° la imagen mostrada en la Captura 1.
Captura 5. A cualquier imagen se le puede agregar un texto. Puede servir para mostrar datos y también como «marca de agua» para proteger a la imagen de ser copiada.
Captura 6. Cualquier parte de una imagen se puede hacer transparente. En este caso se puso transparente todo lo que tenía color de fondo blanco, pero podrías elegir otro color si quieres.
Captura 7. Se cambió el tamaño de la imagen original de 1366 x 768 (que puedes ver en la Captura 1.) al nuevo tamaño de 640 x 480
Captura 8. Se le insertó una imagen de carita feliz a la imagen original de la playa que se mostró en la Captura 3.

Programas

En el archivo IMAGENES.PRG se encuentran las funciones que nos permiten realizar todas las tareas mostradas arriba. Cada función devuelve .T. si finalizó con éxito o .F. si ocurrió algún problema. En el archivo MAIN.PRG se llama a esas funciones.

Conclusión

En este artículo se mostraron algunas de las tareas que se pueden realizar con la biblioteca de clases gratuita _GDIPLUS.VCX del Visual FoxPro.

Hay muchas más tareas que podrías realizar si te interesa trabajar con imágenes. Y hay mucha documentación en Internet y vídeos en YouTube para que profundices tus conocimientos.

Captura 9. Algo que se puede hacer con _GDIPLUS.VCX es ponerles transparencias parciales a las imágenes, como muestra la flecha.

Descargas

Puedes descargar un archivo .ZIP que contiene el archivo MAIN.PRG y el archivo IMAGENES.PRG además de SYSTEM.APP y _GDIPLUS.VCX y algunas imágenes desde:

Después de descargarlo y descomprimirlo escribe DO MAIN en la ventana de comandos del Visual FoxPro para crear algunos archivos de imágenes en esa carpeta, que podrás ver luego.

Artículos relacionados

Mostrar el contenido de un campo memo en una grilla

Cuando insertamos una grilla en un formulario y una de las columnas de esa grilla es un campo memo entonces en tiempo de ejecución vemos algo así:

Captura 1. Cuando una de las columnas de la grilla corresponde a un campo memo.

La palabra «Memo» en la columna N° 5 nos indica que allí hay un campo memo. Haciendo doble clic sobre esa palabra «Memo» veremos una ventanita con el texto correspondiente, algo como:

Captura 2. Al hacer doble clic sobre la palabra «Memo»

Otra alternativa para ver el contenido del campo memo, es insertar un control EditBox en nuestro formulario y entonces tendríamos algo así:

Captura 3. Insertando un control «EditBox» en el formulario para ver allí el contenido del campo memo

Una tercera alternativa, que muy pocos programadores conocen (por lo que he visto durante un montón de años) es rodear el nombre del campo que colocamos en la propiedad ControlSource de la columna de la grilla con paréntesis.

Entonces, si en la columna N° 5 no queremos ver la palabra «Memo» porque preferimos ver las primeras palabras de ese campo y el nombre de nuestra columna es EMP_COMENT en la propiedad ControlSource escribimos: (EMP_COMENT)

Los paréntesis hacen la magia.

Captura 4. Si en la propiedad ControlSource escribimos el nombre del campo rodeado por paréntesis entonces podemos ver el contenido del campo memo.

Los paréntesis sirven para que escribamos una expresión, y no el nombre de un campo o de una variable, en la propiedad ControlSource. Por ejemplo, si queremos ver solamente los 10 primeros caracteres entonces podríamos escribir algo como: (LEFT(EMP_COMENT, 10))
Y así, solamente los primeros 10 caracteres del campo memo EMP_COMENT serían visibles en la columna.

Hay una pequeña contra, y es que la columna se convierte en «ReadOnly» cuando usamos paréntesis.

Todo esto está muy bien pero…¿y si queremos ver todo el campo memo?

En ese caso crearemos un nuevo control, el cual contendrá un control EditBox y una imagen.

Captura 5. Creamos una clase para mostrar en ella todo el contenido del campo memo

En el método DBLCLICK() del control Text1 de la columna que contiene al campo memo escribimos:

Listado 1. El método DBLCLICK() del control Text1

LOCAL loColumna


#DEFINE PEM_EXISTENTE 5

loColumna = This.Parent

IF !PEMSTATUS(ThisForm, "MiEdit", PEM_EXISTENTE) THEN && Si no existe "MiEdit", entonces
ThisForm.AddObject("MiEdit", "EditaMemo") && Agrega al formulario un control "EditaMemo" con el nombre "MiEdit"
ENDIF

WITH ThisForm.MiEdit
.DO_MOSTRAR(This.Parent.Parent, This.Parent) && Muestra el control "MiEdit", se le envían la grilla y la columna actual como parámetros
.Edit1.ControlSource = CHRTRAN(loColumna.ControlSource, "()", "") && Para que en el control "MiEdit" se muestre todo el campo memo
ENDWITH && Se puede quitar la función CHRTRAN() para que el control sea "ReadOnly"

RETURN
*
*

Lo que se hizo en el Listado 1. fue agregarle al formulario un objeto de tipo «EditaMemo». Ese objeto es el encargado de mostrar todo el contenido del campo memo. A continuación se ejecutó el método DO_MOSTRAR() el cual se encarga de establecer las dimensiones y la posición del objeto «EditaMemo» y finalmente se asignó la propiedad «ControlSource» para que se pueda ver el campo memo.

El resultado que obtendremos cuando el usuario haga doble clic sobre una columna que tiene al campo memo será el siguiente:

Captura 6. Al hacer doble clic sobre una celda de la columna donde está el campo memo se muestra todo el contenido de ese campo memo.

Conclusión

Si en una columna de una grilla hay un campo memo entonces tenemos varias alternativas:

  1. Mostrar la palabra «Memo» y cuando el usuario hace doble clic mostrar todo el contenido del campo memo
  2. Mostrar la palabra «Memo» en la grilla y en un control EditBox del formulario mostrar el contenido del campo memo
  3. Mostrar los primeros caracteres del campo memo en la grilla. Para hacer esto el nombre del campo que escribimos en la propiedad ControlSource debe estar rodeado por paréntesis. El defecto es que esa columna se convierte automáticamente en «ReadOnly»
  4. Mostrar los primeros caracteres del campo memo en la grilla y cuando el usuario hace doble clic en una celda mostrarle en un contenedor un control EditBox con todo el contenido del campo memo.

El problema con la alternativa 1 es que obligamos al usuario a hacer clic en la palabra «Memo» para que pueda ver el contenido de ese campo memo. El problema con la alternativa 2 es que estamos usando espacio de nuestro formulario, un espacio valioso que podríamos usar para otras cosas. El problema con la alternativa 3 es que el usuario solamente ve los primeros caracteres del campo memo.

Teniendo todo eso en cuenta la alternativa 4 es la mejor ya que nos permite: mostrar siempre los primeros caracteres del campo memo y mostrarlo completo cuando el usuario lo desea.

La ventaja de hacerlo así es que el usuario no verá la palabra «Memo», lo que verá serán los primeros caracteres que hay en ese campo memo y si quiere ver los demás caracteres entonces debe hacer doble clic en esa celda.

Por lo tanto siempre verá algo y ya será su decisión ver más o no. Y nosotros evitaremos usar un control EditBox que nos quita espacio valioso del formulario.

Descargas

Puedes descargar un archivo .ZIP que contiene todos los archivos usados en este artículo desde:

https://www.mediafire.com/file/t5odwnhsza0h6u3/CAMPO+MEMO+EN+GRILLA.zip/file

Para verlo en funcionamiento, en la ventana de comandos del Visual FoxPro escribe:

DO MAIN

Artículos relacionados

El índice del blog VFPavanzado

Mostrando imágenes con fondo transparente en los botones

¿Te has preguntado alguna vez por qué cuando colocas una imagen en un control CommandButton aunque la imagen sea un .PNG transparente aparece con un desagradable fondo blanco?

La mala noticia es que se trata de un «bug» del Visual FoxPro.

La buena noticia es que en este artículo aprenderás como solucionar ese problema.

Captura 1. Botones con imágenes sin fondo transparente

Todas las imágenes mostradas en los 6 botones de la Captura 1. son archivos .PNG transparentes pero sin embargo puedes ver que todas las imágenes tienen un fondo blanco. ¿Por qué?

El motivo es que un «bug» del Visual FoxPro hace que a veces reconozca el fondo transparente y a veces no. Cuando aún no se ha mostrado una imagen (de un control Image, por ejemplo) o cuando la imagen que fue mostrada no tiene transparencia entonces todas las demás imágenes serán sin transparencia. Y verás el desagradable fondo blanco.

La solución es por lo tanto:

Primero, asignarle a un control Image una imagen transparente y después asignar esa misma imagen al CommandButton.

Y como consecuencia la imagen cuyo nombre se colocó en la propiedad Picture del CommandButton será mostrada con su transparencia.

El control Image no necesita ser visible o sea que el usuario ni se enterará que tal control existe en el formulario.

IMPORTANTE: Como es lógico esta técnica solamente funcionará si la imagen tiene transparencia. Si no tiene transparencia, no esperes milagros porque no los encontrarás.

En el caso de los archivos .BMP hay otra solución. El Visual FoxPro por sí mismo crea un archivo .MSK (archivo de máscara de los .BMP) para el color blanco de los archivos .BMP o sea que cualquier color blanco de un archivo .BMP será transparente. El problema es que los archivos .BMP son muy pesados, ocupan mucho espacio en el disco duro y evidentemente también en la memoria RAM aunque en esta época con discos duros y memorias RAM de capacidades gigantescas tal cosa no es un gran problema.

Para no escribir lo mismo en cada botón que deseamos tenga una imagen transparente lo más conveniente es crear una clase que descienda de CommandButton y que realice las tareas que necesitamos. Entonces luego en nuestros formularios simplemente agregaremos botones de la clase que acabamos de crear.

Listado 1. Creando un botón personalizado

CREATE CLASS MIBOTON OF CONTROLES AS COMMANDBUTTON

Donde «MIBOTON» es el nombre de nuestra nueva clase, «CONTROLES» es el nombre de la biblioteca de clases (su extensión será .VCX), y «COMMANDBUTTON» es el nombre de la clase padre. Eso significa que «MIBOTON» heredará todas las propiedades y todos los métodos de la clase «COMMANDBUTTON».

NOTA: Desde luego que «MIBOTON» y «CONTROLES» son los nombres elegidos para este artículo, tú puedes elegir cualquier otro nombre que prefieras.

Listado 2. El método INIT() de la clase MIBOTON

  This.DO_PONER_TRANSPARENCIA()
  
RETURN
*
*

En realidad no necesitas un método nuevo para esto, podrías escribir todo el código dentro del método INIT() del botón pero por claridad el autor de este blog prefiere hacerlo como se muestra en el Listado 2. Es más entendible.

Listado 3. El método DO_PONER_TRANSPARENCIA() del botón

LOCAL loImagen
  
  WITH This
    IF !EMPTY(.cImagen) THEN
      loImagen           = Createobject("IMAGE")
      loImagen.BackStyle = 0     && 0=Transparente
      loImagen.Picture   = .cImagen
      loImagen.Stretch   = 2
      .Picture           = loImagen.Picture
      loImagen           = .NULL.
      RELEASE loImagen
    ENDIF
  ENDWITH
  
RETURN
*
*

Como puedes ver al botón le agregamos una propiedad llamada cImagen y es en dicha propiedad donde pondremos los nombres de los archivos .PNG transparentes que deseamos mostrar en el botón.

¿Por qué eso?

Porque si escribimos el nombre de la imagen en la propiedad Picture de nuestro botón entonces nuestra técnica no funcionará. Recuerda que el Visual FoxPro pone la primera transparencia que encontró y al crearse el botón y tener el nombre de un archivo en la propiedad Picture entonces mostrará a la imagen sin transparencia. Y lo que queremos es justamente lo contrario. Por eso la propiedad Picture del botón debe estar en blanco en nuestro formulario, el valor de dicha propiedad le será asignado por el método DO_PONER_TRANSPARENCIA() al crearse el botón.

Captura 2. Poniendo botones de clase MIBOTON en el formulario

Cuando estamos desarrollando nuestra aplicación veremos a nuestro formulario como en la Captura 2. Ninguna imagen aparece en los botones.

Captura 3. Las imágenes que verá el usuario

Cuando se ejecuta nuestra aplicación las imágenes de los botones son visibles, tal como lo muestra la Captura 3.

Si comparas la Captura 1. con la Captura 3. seguramente coincidirás en que la Captura 3. es más atractiva a la vista.

El defecto de esta técnica es que mientras estamos desarrollando nuestra aplicación no vemos las imágenes en los botones (tal y como podemos confirmar volviendo a mirar la Captura 2.). Una solución es poner las imágenes en las propiedades Picture de los botones hasta que estemos listos para entregar nuestra aplicación al cliente y en ese momento eliminarlas de la propiedad Picture y colocarlas en la propiedad cImagen.

Y es que si están en la propiedad Picture los fondos se muestran sin la transparencia. Y no queremos eso.

Captura 4. En la propiedad cImagen de cada botón se debe escribir el nombre del archivo .PNG transparente que se desea mostrar en ese botón

Resumiendo

  1. Se crea una clase que desciende de CommandButton para que herede todas las propiedades y todos los métodos que tiene CommandButton
  2. Se le agrega a la nueva clase una propiedad llamada cImagen
  3. Se agrega un método a la nueva clase, llamado DO_PONER_TRANSPARENCIA()
  4. Se invoca al método DO_PONER_TRANSPARENCIA() en el método INIT() de la nueva clase
  5. Al formulario se le agrega un botón de la nueva clase
  6. En la propiedad cImagen de ese botón se escribe el nombre del archivo .PNG transparente que se desea mostrar en ese botón
  7. Se repite desde el paso 5. para todos los demás botones

La tarea que realiza el método DO_PONER_TRANSPARENCIA() es crear un control de tipo Image al cual se le pone la propiedad BackStyle en transparente y se le dice que la imagen que debe mostrar (aunque en realidad nunca la ve el usuario porque ese control imagen siempre permanece invisible) es la que se encuentra en la propiedad cImagen de ese mismo botón. Luego, en la propiedad Picture del botón se pone la imagen (la cual como recordarás es transparente) que se encuentra en la propiedad Picture del control de tipo Image. Como ese control de tipo Image ya cumplió su misión y ya no es necesario, se lo elimina de la memoria.

Y así, se consigue el efecto buscado.

Conclusión

Podemos mejorar un poco la apariencia de nuestras aplicaciones si las imágenes que colocamos en los botones no tienen fondo blanco. O sea, si son transparentes.

Eso podemos conseguirlo de 2 maneras:

  1. Usando archivos .BMP con fondo blanco porque el Visual FoxPro automáticamente crea un archivo .MSK para que el color blanco sea transparente.
  2. Usando archivos .GIF transparentes o .PNG transparentes y empleando la técnica mostrada en este artículo.

NOTA 1: Recuerda que las imágenes que deseas mostrar en los botones deben tener fondos transparentes si usas archivos .GIF o .PNG. De lo contrario esta técnica no funcionará. No hace magia.

NOTA 2: Es costumbre del autor de este blog nombrar a los archivos de imágenes con su resolución y una letra «t» opcional que indica que esa imagen es transparente. Por ejemplo: «agregar32t.png» indica que el tamaño es de 32×32 pixeles y que tiene fondo transparente. «cubos24t.png» indica que el tamaño es de 24×24 pixeles y que tiene fondo transparente. Es una costumbre que facilita la vida.

Descargas

Puedes descargar el formulario MAIN.SCX, la biblioteca de clases CONTROLES.VCX, y los archivos de imagen utilizados en este artículo desde este archivo .ZIP:

https://www.mediafire.com/file/zw95y81s82f90pk/FONDO+TRANSPARENTE+EN+COMMANDBUTTON.zip/file

Artículos relacionados

El índice del blog VFPavanzado

Rich Text Format

Hay un control que viene incluido con el Visual FoxPro desde su primera versión pero que es muy poco utilizado a pesar de lo poderoso que es: Rich Text Format.

¿Para qué sirve?

Para que el usuario pueda escribir texto en un formulario de Visual FoxPro como si lo estuviera haciendo en un documento Word, Excel, o similares.

No es un control nativo del Visual FoxPro, es un control ActiveX, eso significa que se lo debe instalar antes de usarlo. Para ello:

Tools | Options… | Controls | ActiveX Controls

Buscas: Microsoft Rich Textbox Control, versión 6.0

y lo seleccionas. Deberías ver algo así:

Captura 1. Debes buscar el control ActiveX y luego seleccionarlo

(1) Buscas entre todos los controles ActiveX el llamado Microsoft Rich Textbox Control, versión 6.0

(2) Haces clic en él, para seleccionarlo

(3) Clic en el botón «OK» para poder utilizar ese control ActiveX en tus formularios del Visual FoxPro

Un formulario para mostrarlo en funcionamiento

Para que puedas ver este control ActiveX funcionando hice un pequeño formulario que lo utiliza.

Captura 2. Un formulario que muestra al control Rich Text Format en pleno funcionamiento

El contenido del control puede grabarse en un archivo, si quieres. Ese archivo podrás leerlo y editarlo con Word. También puedes mostrar en el control el contenido de un archivo RTF que fue creado con Word (o con este formulario).

Otra alternativa es enviar el contenido del control a la impresora. Normalmente eso no se hace porque se lo imprime desde Word pero si quieres o necesitas hacerlo, tienes el código fuente para ver una de las posibles formas de imprimirlo.

Propiedades

SelFontName. Especifica el nombre de la fuente que se aplicará al texto que esté seleccionado.

SelFontSize. Especifica el tamaño de la fuente que se aplicará al texto que esté seleccionado.

SelBold. Cambia a negritas o a normal el texto que esté seleccionado.

SelItalic. Cambia a itálica o a normal el texto que esté seleccionado.

SelUnderline. Subraya o quita el subrayado del texto que esté seleccionado.

SelColor. Cambia el color del texto que esté seleccionado.

Métodos

LOADFILE(). Lee un archivo y su contenido lo coloca en el control RTF.

SAVEFILE(). Graba el contenido del control RTF en un archivo.

SELPRINT(). Imprime el texto que está seleccionado.

Descargas

Puedes descargar el formulario que muestra en la práctica el funcionamiento del control Rich Text Format desde:

https://www.mediafire.com/file/sy9e8lvugp1mte6/CONTROL_RTF.zip/file

Artículos relacionados

El índice del blog VFPavanzado

W_Grilla. Una grilla mejorada (3)

En los dos artículos anteriores de esta serie ya hemos visto algunas de las características y funcionalidades de W_Grilla, ahora veremos las que aún faltan.

Captura 1. El menú contextual aparece cuando el usuario hace clic derecho sobre una celda de la grilla

(1) Insertar fila

Inserta una fila en blanco en la posición del cursor. Muy útil cuando el usuario ingresa directamente datos en la grilla.

Captura 2. Haciendo clic en «Insertar fila» se inserta una fila en blanco
Captura 3. Se puede hacer clic en «Insertar fila» varias veces seguidas para insertar varias filas

(2) Eliminar fila

Así como usando el menú contextual se puede insertar una fila también se tiene la posibilidad de eliminar una fila. Si se hace clic varias veces seguidas en «Eliminar filas» entonces se pueden eliminar varias filas.

(3) Primera fila

Se ubica en la primera fila de la grilla

(4) Subir una fila

Sube un lugar la fila actual y la que se encontraba allí baja un lugar

(5) Bajar una fila

Baja un lugar la fila actual y la que se encontraba allí sube un lugar

(6) Última fila

Se ubica en la última fila de la grilla

(7) Copiar celda

Copia el contenido de la celda en el portapapeles. Entonces el usuario puede luego pegar ese contenido en cualquier otro programa (Bloc de notas, Excel, Word, un campo de texto, etc.)

(8) Pegar en la celda

Coloca en la celda actual el contenido del portapapeles

Ponerle marcas de chequeo a una columna

Un efecto muy vistoso y que le gusta muchos a los usuarios es cuando una columna de la grilla tiene controles Checkbox. Haciendo clic sobre uno de esos controles el usuario puede marcarlo o desmarcarlo. Y haciendo clic sobre el «Header» de esa columna puede marcarlos a todos o desmarcarlos a todos.

Captura 4. A una columna se le pueden colocar controles Checkbox para que sea más atractiva
Captura 5. Haciendo clic sobre el «Header» de una columna se marcan todas las filas o se desmarcan todas las filas

Los métodos públicos de W_Grilla

En W_Grilla existen métodos públicos y métodos protegidos. Los métodos públicos son los que puedes llamar desde tus aplicaciones. Los métodos protegidos son para el uso interno de W_Grilla.

Los métodos públicos que empiezan con DO_ realizan algún procesamiento y los que empiezan con SET_ cambian el valor de alguna propiedad.

DO_CAMBIAR_COLOR_FILA()

Permite cambiar los colores de las filas según las condiciones especificadas. Por ejemplo: color rojo si se ganó poco dinero, color azul si la ganancia es normal, color verde si se ganó mucho dinero.

DO_CELDA_COPIAR()

Coloca en el portapapeles el contenido de la celda actual.

DO_CELDA_PEGAR()

Coloca en la celda actual el contenido del portapapeles

DO_COLORES_ALTERNADOS()

Muestra las filas de las grillas con sus colores alternados. Es decir, las filas impares con un color y las filas pares con otro color. Esos colores pueden ser definidos en el archivo GLOBALES.H (CLR_GRILLA_FILA_BACK_1, CLR_GRILLA_FILA_BACK_2, CLR_GRILLA_FILA_FORE_1, CLR_GRILLA_FILA_FORE_2) o enviados como argumentos del método SET_COLORES_ALTERNADOS()

DO_CONFIGURAR()

Configura la grilla para que sea mostrada al usuario con la apariencia deseada. Los valores por defecto se deben especificar en el archivo GLOBALES.H

DO_FILA_BAJAR()

Baja a la siguiente fila. La fila que se encontraba allí sube un lugar

DO_FILA_ELIMINAR()

Elimina (borra) la fila actual

DO_FILA_INSERTAR()

Inserta una fila en blanco en la posición de la fila actual, la cual baja un lugar

DO_FILA_PRIMERA()

Se ubica en la primera fila de la grilla

DO_FILA_SUBIR()

Sube a la fila anterior. La fila que se encontraba ahí baja un lugar

DO_FILA_ULTIMA()

Se ubica en la última fila

DO_GUARDAR_COLORES_EN_ARRAY()

Coloca los colores del «Header» de cada columna en un array para que se pueda saber fácilmente cuales eran esos colores originalmente

DO_MOSTRAR_TOTALES()

Coloca debajo de las columnas numéricas sus respectivas sumas. Se indica cuales son las columnas numéricas cuyas sumas se quieren ver enviando como argumento del método SET_SUMAR_COLUMNAS() los nombres de los campos correspondientes.

SET_ANCHO_AUTOMATICO()

Por defecto el ancho de la grilla es puesto automáticamente. Si se desea ponerlo manualmente entonces hay que enviar .F. como argumento de este método.

SET_CHECAR_COLUMNAS()

Si se quiere mostrar controles Checkbox en una columna entonces hay que enviar el nombre de esa columna como argumento de este método. Se pueden enviar los nombres de varias columnas como argumentos.

SET_COLORES_ALTERNADOS()

Si se quiere tener colores alternados o que esos colores no sean los colores por defecto. Los colores por defecto se definen en el archivo GLOBALES.H

SET_COLORES_EXCEL()

Los colores de las celdas en las planillas Excel si no se quieren usar los colores por defecto. Los colores por defecto de las planillas Excel se definen en el archivo GLOBALES.H

SET_COLORES_ORIGINALES()

Los colores que tiene la grilla cuando no se le definen colores alternados ni colores personalizados.

SET_COLORES_VENTANA_FILTRO()

Los colores de la ventana de filtro cuando no se quieren usar los colores originales de dicha ventana. Los colores originales se definen en el archivo GLOBALES.H

SET_CONFIGURAR_EXCEL()

Cuando se quiere que las planillas Excel generadas estén basadas en un modelo de planilla. En ese modelo de planilla se colocan textos, bordes, colores, etc., y se le envían los valores que se quieren colocar en esas celdas.

SET_FILTRAR_FILAS()

Si se quiere que el usuario pueda filtrar filas (o sea: que se le muestre la ventanita de filtro) o no. Por defecto sí puede filtrar.

SET_ICONOS_ORDENAMIENTO()

Cuando el usuario hace clic izquierdo sobre el «Header» de una columna la grilla es ordenada según esa columna, de forma ascendente o descendente. Y se le muestra un iconito que le indica que la grilla está ordenada y la forma (ascendente o descendente). Esos dos iconitos pueden cambiarse. Los nombres de los nuevos iconitos deben enviarse como argumentos de este método.

SET_MOSTRAR_MENU_CONTEXTUAL()

Determina si el usuario puede ver el menú contextual o no. El menú contextual se muestra cuando se hace clic derecho sobre alguna celda de la grilla. Por defecto sí puede ver al menú contextual.

SET_OPCIONES_MENU_CONTEXTUAL()

Cuando el usuario hace clic derecho sobre cualquier celda de la grilla se le muestra un menú contextual que le permite realizar algunas tareas. Con este método se puede determinar cuales opciones tendrá visibles. Por defecto todas las opciones están visibles (y por lo tanto son elegibles) pero se pueden ocultar opciones para que en algunos casos no tenga acceso a ellas.

SET_ORDENAR_COLUMNAS()

Por defecto, cuando el usuario hace clic izquierdo sobre el «Header» de una grilla se ordena a la grilla según esa columna. Se puede evitar que ordene a la grilla enviando .F. como argumento de este método.

SET_SIMBOLO_INCLUSION()

Cuando en la ventanita de filtro el usuario escribe algo en el control Textbox y ese campo es alfanumérico entonces puede escribir también un carácter que indica que el texto puede encontrarse en cualquier parte del campo: al principio, en el medio, al final. Por ejemplo si escribe: ana$ entonces se mostrarán las filas donde estén: «Anabella», «Ana Soledad», «Luciana», «Ninfa Mariana», «Susana Noemí». Por defecto el símbolo de inclusión es el $ pero puedes enviar como argumento de este método a cualquier otro símbolo. Por ejemplo: %, &, |, ?, /

SET_SUMAR_COLUMNAS()

Muchas veces el usuario necesita ver debajo de una columna numérica la suma de los números que se encuentran en esa columna. Para ello solamente debes enviar como argumentos de este método a los nombres de los campos cuyas sumas deseas mostrar.

Conclusión

El control Grid del Visual FoxPro es muy poderoso pero puede ser mejorado. En esta serie de artículos se mostró un nuevo control, llamado W_Grilla, y que desciende del control Grid nativo.

Se le agregaron muchas características y funcionalidades que los usuarios normalmente necesitan.

Usarlo, te facilitará la vida a ti como programador y a los usuarios de tus aplicaciones les ayudará mucho con sus tareas.

Sin embargo tiene un defecto y hay que advertírtelo: el usuario que usa a W_Grilla durante uno o dos días nunca más quiere usar un control Grid nativo porque lo encuentra muy limitado. Así que te pedirá que todas tus grillas sean W_Grilla y eso a veces no es posible hacer en poco tiempo. La parte positiva es que por agregar a W_Grilla podrás cobrar dinero adicional.

Descargas

Puedes descargar un archivo .ZIP que contiene a la biblioteca de clases CONTROLES.VCX, al archivo cabecera de constantes declaradas GLOBALES.H, al formulario PEDIR_RANGO, a un modelo de planilla Excel de ejemplo, a dos archivos .PRG de ejemplo, a dos formularios de ejemplo, y a varios archivos gráficos desde:

https://www.mediafire.com/file/bxnts450d3oop6n/W_GRILLA.zip/file

En la ventana de comandos del Visual FoxPro debes escribir:

DO MAIN1 && Para ejecutar a un formulario demo

DO MAIN2 && Para ejecutar al otro formulario demo

Artículos relacionados

W_GRILLA. Una grilla mejorada (1)

W_GRILLA. Una grilla mejorada (2)

El índice del blog VFPavanzado

W_Grilla. Una grilla mejorada (2)

Una de las características de W_Grilla que gusta muchísimo a los usuarios es la posibilidad de enviar los datos que ven en la grilla a una planilla Excel. Y es que la gran mayoría de los usuarios saben usar Excel y usan Excel con bastante frecuencia entonces tener allí sus datos les resulta muy cómodo.

W_Grilla tiene 2 modos de enviar los datos a Excel:

  1. Modo simple
  2. Modo con modelo

En el modo simple lo mismo que el usuario ve en la grilla es lo que ve en su planilla Excel. Es útil cuando solamente quiere controlar algo y probablemente no guardará esa planilla para un uso posterior, solamente quiere mirar los datos, verificarlos, y listo.

En el modo con modelo se escriben (generalmente en las primeras filas de la grilla) algunos datos que necesita visualizar el usuario para entender mejor a la grilla. Por ejemplo: nombre de la Empresa, nombre de la Sucursal, nombre del Cliente, nombre del Proveedor, nombre del Vendedor, fecha, hora, año, mes, etc. O sea datos que aumentan su comprensión de la planilla. Y es muy usual que quiera grabar esa planilla en su disco duro para visualizarla más tarde.

Modo simple

Es el modo por defecto y nada debes hacer para que el usuario genere planillas Excel con este modo. Cuando haga clic en el iconito de Excel se generará una planilla que mostrará los mismos datos que tiene la grilla.

Captura 1. Para generar una planilla Excel el usuario debe hacer clic en ese iconito.
Captura 2. La planilla Excel generada es muy simple pero contiene todos los datos que contiene la grilla

Como puedes ver, la planilla Excel mostrada en la Captura 2. es muy sencilla, solamente tiene los títulos de las columnas y datos en cada celda que coinciden con los datos de las celdas de la grilla.

NOTA: Si la grilla tenía sumas de sus columnas numéricas entonces esas mismas sumas son también mostradas en la planilla Excel.

Captura 3. Si debajo de la grilla se mostraban las sumas de sus columnas numéricas, esas mismas sumas también son mostradas en la planilla Excel.

Modo con modelo

En el modo con modelo podemos tener planillas Excel mucho más vistosas. La idea aquí es tener un modelo de planilla predefinido, el cual puede tener celdas con distintos FontName, FontSize, BackColor, ForeColor, bordes, etc. Esta clase de planillas por supuesto que le gustan mucho más al usuario.

Listado 1. Configurando Excel para usar un modelo de planilla

LOCAL lcNombreModeloPlanilla, lcNombreNuevaPlanilla, lnFilaInicial
  
  lcNombreModeloPlanilla = SYS(5) + CURDIR() + "MODELO_VENTAS_REALIZADAS_1.XLSX"
  lcNombreNuevaPlanilla  = SYS(5) + CURDIR() + "VENTAS_DE_MARIA_MARCELA.XLSX"
  lnFilaInicial          = 7
  
  DIMENSION laDatos[5, 6]
  
  laDatos[1, 1] = "VENTAS POR VENDEDOR"
  laDatos[3, 3] = "María Marcela Martínez"
  laDatos[5, 3] = DATE()
  laDatos[5, 6] = LEFT(TIME(), 5)
  
 
  WITH ThisForm.W_GRILLA1
    .SET_CONFIGURAR_EXCEL(lcNombreModeloPlanilla, lcNombreNuevaPlanilla, lnFilaInicial, @laDatos)
    .DO_CONFIGURAR()
    .SetFocus()
  ENDWITH

En este caso ya no es todo automático, debemos indicar de forma manual algunos de los datos para la planilla Excel.

El nombre del modelo de planilla y el nombre de la planilla deben estar completos, o sea con las rutas respectivas. Esa es una limitación del Excel, no le gustan los nombres de archivos que no tienen rutas.

Hay que indicar la fila inicial porque ya no será la fila número 1 como en el caso del modo simple, aquí será un número mayor que 1, el número de la fila donde queremos que aparezcan los «Headers» de las columnas. En este ejemplo es 7, en tu propio modelo de planilla puede ser cualquier otro número.

Luego hay que dimensionar el array que contendrá los datos que aparecerán en la planilla Excel. En este ejemplo se pusieron 5 y 6. El 5 es el número de la última fila donde hay algún dato y el 6 es el número de la última columna donde hay algún dato.

A continuación se asignaron a los elementos del array los valores que nos interesan:

laDatos[1, 1] significa que queremos verlo en la fila 1, columna 1, de la planilla Excel

laDatos[3, 3] significa que queremos verla en la fila 3, columna 3, de la planilla Excel

laDatos[5, 3] significa que queremos verlo en la fila 5, columna 3, de la planilla Excel

Y finalmente se ejecuta al método SET_CONFIGURAR_EXCEL() enviándole como argumentos el nombre del modelo de planilla Excel, el nombre de la nueva planilla Excel, el número de la primera fila donde debe mostrar los datos de la grilla, y los datos que debe colocar en la cabecera de la nueva planilla Excel. Si nuestro modelo de planilla Excel es así:

Captura 4. Un modelo de planilla Excel

entonces lo que obtendremos será algo así:

Captura 5. Esta planilla fue generada basándose en el modelo de planilla mostrado en la Captura 4.

En este ejemplo se colocaron en la cabecera de la planilla Excel un título, el nombre de un vendedor, la fecha de generación, y la hora de generación. Por supuesto que en tu caso puedes colocar cualquier otro dato que quieras.

Lo interesante de esto es que escribiendo muy poco puedes hacer que el usuario obtenga la planilla Excel que necesita.

Los colores mostrados en la Captura 5. son los colores por defecto, o sea los colores que especificaste en el archivo GLOBALES.H, pero si quieres usar otros colores también puedes hacerlo.

Listado 2. Configurando Excel para mostrar las planillas con otros colores

LOCAL lcNombreModeloPlanilla, lcNombreNuevaPlanilla, lnFilaInicial

  #DEFINE COLOR_BACKCOLOR_TITULO  6
  #DEFINE COLOR_FORECOLOR_TITULO  RGB( 64,  64,   0)
  #DEFINE COLOR_FORECOLOR_CELDA   RGB( 64, 128,  64)
  #DEFINE COLOR_BACKCOLOR_TOTALES 7
    
  lcNombreModeloPlanilla = SYS(5) + CURDIR() + "MODELO_VENTAS_REALIZADAS_1.XLSX"
  lcNombreNuevaPlanilla  = SYS(5) + CURDIR() + "VENTAS_DE_ERIKA.XLSX"
  lnFilaInicial          = 7
  
  DIMENSION laDatos[5, 6]
  
  laDatos[1, 1] = "VENTAS EN LA SUCURSAL: Asunción - Paraguay"
  laDatos[3, 3] = "Erika Schmidt"
  laDatos[5, 3] = DATE()
  laDatos[5, 6] = LEFT(TIME(), 5)
  
  WITH ThisForm.W_GRILLA1
    .SET_COLORES_EXCEL(COLOR_BACKCOLOR_TITULO, COLOR_FORECOLOR_TITULO, COLOR_FORECOLOR_CELDA, COLOR_BACKCOLOR_TOTALES)
    .SET_CONFIGURAR_EXCEL(lcNombreModeloPlanilla, lcNombreNuevaPlanilla, lnFilaInicial, @laDatos)
    .DO_CONFIGURAR()
    .SetFocus()
  ENDWITH

En el Listado 2. se quiere usar otros colores, entonces se envían esos colores como argumentos del método SET_COLORES_EXCEL(). El resultado que obtendremos será el siguiente:

Captura 6. Se pueden cambiar los colores predeterminados en el archivo GLOBALES.H por cualesquiera otros colores
Captura 7. También las sumas de las columnas pueden cambiar de color

Sin preocupaciones, sin pérdida de tiempo, podemos generar planillas Excel muy rápidamente y de mucha utilidad para el usuario.

Y por ahora…

ya termino este artículo. En el siguiente mostraré las características de W_GRILLA que aún no he mostrado y también podrás descargar su código fuente para mirarlo en funcionamiento. Y eventualmente para implementarlo en tus aplicaciones.

Artículos relacionados

El índice del blog VFPavanzado

W_Grilla. Una grilla mejorada (1)

El control grid del Visual FoxPro es una maravilla, muy poderoso, mucho más poderoso que sus equivalentes en otros lenguajes de programación. Pero como todo en esta vida, puede ser mejorado. Y en este artículo veremos un control llamado W_GRILLA que desciende (o sea: está subclaseado) de un control grid.

Este control W_GRILLA aumenta la funcionalidad del control grid nativo, le otorga características que el control grid nativo no posee y de esta manera le hace la vida más fácil a los programadores y a los usuarios.

Lo que con W_GRILLA se puede hacer y muy fácilmente es:

  1. Mostrar las filas con colores alternados
  2. Mostrar las filas con distintos colores, dependiendo de algunas condiciones
  3. Sumar columnas
  4. Ordenar una columna de forma ascendente o descendente
  5. Mostrar todos los valores de una columna
  6. Mostrar las filas que cumplen con una condición
  7. Mostrar las filas que tienen un texto al principio, en el medio, o al final
  8. Mostrar las filas que cumplen con un operador de comparación
  9. Mostrar las filas que se encuentren dentro de un rango de valores
  10. Enviar las filas a una planilla Excel
  11. Insertar una fila
  12. Eliminar una fila
  13. Subir hasta la primera fila
  14. Subir un lugar una fila
  15. Bajar un lugar una fila
  16. Bajar hasta la última fila
  17. Copiar en el portapapeles el contenido de una celda
  18. Pegar en una celda el contenido del portapapeles
  19. Ponerle marcas de chequeo a una columna

Para que todo esto se entienda mejor podrás descargar un archivo .ZIP con todo el código fuente necesario para hacer tus pruebas y luego agregar W_GRILLA a tus aplicaciones.

En el ejemplo se creó un cursor llamado VENTAS y se le insertaron 30 filas.

El archivo GLOBALES.H

Una muy buena práctica de programación es utilizar constantes declaradas. En Visual FoxPro las declaramos con la directriz #DEFINE y podemos guardar a muchos #DEFINE en un archivo cabecera de constantes declaradas. Es norma (aunque no es obligatorio) que la extensión de ese archivo sea .H (por «Header», cabecera, en inglés). Al utilizado en este artículo se le ha dado el nombre de GLOBALES.H pero tú puedes darle cualquier otro nombre, si quieres.

(1) Mostrar las filas con colores alternados

Para obtener este efecto enviamos .T. como argumento del método SET_COLORES_ALTERNADOS()

Captura 1. Los colores de las filas se muestran de forma alternada

Los colores son definidos en el archivo GLOBALES.H

Listado 1. Los colores de las filas alternas

#DEFINE CLR_GRILLA_FILA_BACK_1   RGB(255, 255, 255)
#DEFINE CLR_GRILLA_FILA_BACK_2   RGB(224, 224, 128)
#DEFINE CLR_GRILLA_FILA_FORE_1   RGB( 32,  64,  32)
#DEFINE CLR_GRILLA_FILA_FORE_2   RGB( 64,  64, 192)

¿por qué se definen los colores en el archivo GLOBALES.H?

porque haciendo así todas tus grillas en una aplicación pueden tener los mismos colores y tu diseño se verá muy consistente. Pero si en algún caso deseas usar otros colores eso lo conseguirás ejecutando al método SET_COLORES_ALTERNADOS() y enviándole como argumentos los colores que prefieres.

(2) Mostrar las filas con distintos colores, dependiendo de algunas condiciones

Para obtener este efecto:

  • Enviamos .T. como argumento del método SET_COLORES_PERSONALIZADOS()
  • En el método DO_CAMBIAR_COLOR_FILA() de nuestra grilla ponemos las condiciones

Listado 2. Las condiciones para mostrar las filas con distintos colores

LOCAL lnColor
  
  #DEFINE VENTAS_POCAS    RGB(255, 128, 128)
  #DEFINE VENTAS_NORMALES RGB(192, 192, 255)
  #DEFINE VENTAS_MUCHAS   RGB(128, 255, 128)
  
  lnColor = 0
  
  DO CASE
    CASE VTC_VENDID < 200000
      lnColor = VENTAS_POCAS
    CASE VTC_VENDID < 500000
      lnColor = VENTAS_NORMALES
    OTHERWISE
      lnColor = VENTAS_MUCHAS
  ENDCASE
  
RETURN (lnColor)
*
*
Captura 2. Mostrando las filas de la grilla con diferentes colores

En el Listado 2. lo que se hizo fue establecer las condiciones para que una fila sea mostrada con un determinado color.

NOTA: El autor de este artículo tiene la costumbre de usar #DEFINE porque considera que así el código fuente es mucho más entendible, pero no es obligatorio usar #DEFINE.

(3) Sumar columnas

Muchas veces queremos ver debajo de una columna numérica la suma de dicha columna. Con W_GRILLA es muy fácil hacerlo. Simplemente al método SET_SUMAR_COLUMNAS() le enviamos como argumentos los nombres de los campos numéricos que deseamos sumar y automáticamente debajo de esas columnas de la grilla se mostrarán sus sumas. Y además con la misma propiedad InputMask que tiene la columna.

Listado 3. Determinando cuales columnas numéricas serán sumadas

  WITH ThisForm.W_GRILLA1
    .SET_SUMAR_COLUMNAS("VTC_VENDID, VTC_COMPRA, VTC_GANANC")
    .DO_CONFIGURAR()
    .SetFocus()
  ENDWITH
Captura 3. De forma automática se colocó la suma de cada columna debajo de ella

Los colores del fondo y del texto están establecidos en el archivo GLOBALES.H y tienen los nombres de CLR_TOTAL_FONDO y de CLR_TOTAL_TEXTO. Desde luego que si no te gustan esos colores puedes cambiarlos por los de tu preferencia.

Listado 4. Los colores de los totales de las columnas

#DEFINE CLR_TOTAL_FONDO  RGB(216, 216,   0)
#DEFINE CLR_TOTAL_TEXTO  RGB(  0,   0, 128)

(4) Ordenar una columna de forma ascendente o descendente

Muchas veces el usuario desea ordenar a la grilla según el contenido de una columna. La forma más intuitiva es haciendo clic en el «Header» de dicha columna. Para que sepa por cual columna está ordenada la grilla y además para que sepa que el orden es ascendente o descendente se muestra una flechita en el «Header» correspondiente.

Captura 4. Haciendo clic sobre un «Header» se ordena la grilla según esa columna, de forma ascendente o descendente
Captura 5. Presionando el botón izquierdo sobre un «Header» se ordena de menor a mayor o de mayor a menor

(5) Mostrar todos los valores de una columna

A veces en una columna no hay valores repetidos y por lo tanto tendrá tantos valores como filas tenga la grilla. Pero a veces sí hay valores repetidos y es interesante ver cuales son. En nuestro ejemplo los números de los comprobantes no están repetidos pero en las demás columnas sí se pueden existir valores repetidos.

Para ver todos los valores que tiene una columna (hasta un máximo de 50, pero puedes cambiar esa cantidad si quieres) se debe presionar el botón derecho sobre el «Header» de la columna que nos interesa.

Captura 6. Al hacer clic derecho sobre un «Header» se muestran todos los valores distintos que hay en esa columna
Captura 7. Al hacer clic sobre uno de los valores mostrados en la lista se muestran todas las filas que coinciden con ese valor
Captura 8. Se puede hacer clic derecho sobre el «Header» de cualquier columna
Captura 9. Al seleccionar un valor en la lista solamente las filas que tienen ese valor son mostradas en la grilla

(6) Mostrar las filas que cumplen con una condición

A veces se necesita buscar un dato en una columna. Puede hacerse como ya fue mostrado en las capturas 6 al 9 pero hay también otra alternativa y es escribiendo en el control Textbox lo que se desea buscar.

Captura 10. Al escribir algo en el «Textbox» se muestran todas las filas que empiezan con lo escrito en el «Textbox»

Fíjate que al escribir «ma» se mostraron todas las filas que empiezan con «ma», en este caso «María Gloria S.A.» y «María Elena S.A.», sin distinción de mayúsculas y minúsculas. Y también se ignoran los acentos. O sea que si en el control Textbox se escribe «maria», «MARIA», «María», «MARÍA», etc., siempre serán encontradas.

Captura 11. Si es una columna numérica se escribe 2 se muestran todas las filas que empiezan con 2
Captura 12. Si en una columna numérica se escribe 24 se muestran todas las filas que empiezan con 24
Captura 13. Si en una columna de tipo fecha se escribe 1 se muestran todas las filas que empiezan con 1

(7) Mostrar las filas que tienen un texto al principio, en el medio, o al final

En el caso de las columnas alfanuméricas también se puede escribir un símbolo de inclusión con lo cual se estará indicando que el texto también puede estar adentro o al final, no solamente al principio.

Captura 14. Al escribir el símbolo $ (que es el símbolo de inclusión por defecto) también se muestra a Fátima S.A.

En la Captura 14. se escribió «ma$» en el control Textbox y eso indica que los caracteres «ma» pueden encontrarse al principio, en el medio, o al final del texto. Por eso también apareció Fátima S.A.

El símbolo de inclusión predeterminado es el $ pero puedes especificar otro si lo deseas enviándolo como argumento al método SET_SIMBOLO_INCLUSION()

El símbolo de inclusión puede escribirse en cualquier parte dentro del Textbox, no es requisito que se encuentre al final del texto.

Captura 15. El símbolo de inclusión también puede encontrarse al inicio o dentro del texto buscado

Como puedes ver en la Captura 15. el símbolo de inclusión también puede escribirse antes que el texto buscado.

(8) Mostrar las filas que cumplen con un operador de comparación

En la ventanita de filtro el usuario puede escribir un valor y se le muestran todas las filas que contienen ese valor. Pero a veces no es lo que necesita. A veces necesita las filas que sean menores, o que sean mayores, o que sean distintas, etc. Para ello debe hacer clic en «Este rango de filas» para que pueda seleccionar un operador de comparación.

Captura 16. Para ver todas las filas donde «Vendido por» sea mayor o igual que 200.000

(9) Mostrar las filas que se encuentren dentro de un rango de valores

En otras ocasiones lo que el usuario necesita es encontrar las filas que se encuentran dentro de un rango de valores. O sea que cumplan con una «condición 1» y/o una «condición 2».

Para esas ocasiones debe especificar dos operadores de comparación y dos valores. Por ejemplo, para ver todas las ventas realizadas entre los días 7 de enero de 2023 y 10 de enero de 2023 podría escribir algo así:

Captura 17. Todas las filas que se encuentran en el rango de fechas especificado

Y por ahora…

Para no hacer este artículo demasiado largo lo dejaré aquí y en el siguiente artículo continuaré mostrando las características de W_GRILLA.

Ya tienes una idea de lo que puede conseguirse.

Lo más interesante es que debes escribir poquísimo para permitirle o impedirle al usuario que use esas características o para implementarlas tú en tus aplicaciones.

La idea es tener a un reemplazante del control Grid pero mucho más poderoso y escribiendo poco o casi nada.

Artículos relacionados

El índice del blog VFPavanzado

Usando la Lista de Tareas

El Visual FoxPro tiene tantas características que aún quienes hace muchos años lo usan como su único (o principal) lenguaje de programación no las conocen a todas.

Una de esas características poco conocidas es la «Task List» o lista de tareas en castellano.

¿Para qué sirve la «Task List»?

Tiene dos utilidades:

  1. Acceder rápidamente a un método de una clase visual
  2. Recordarnos las tareas que debemos realizar (aún cuando no estén relacionadas con la programación de computadoras)

Acceder rápidamente a un método de una clase visual

En cualquier método de una clase visual (por ejemplo: de un formulario) si hacemos clic con el botón derecho sobre una línea veremos el siguiente menú contextual:

Captura 1. Al hacer clic con el botón derecho sobre una línea aparece un menú contextual

Y si hacemos clic sobre la opción «Add Task List Shortcut» entonces a la izquierda de esa línea aparecerá el iconito de una flecha.

Captura 2. El iconito con la imagen de una flecha azul nos indica que esa línea está en la «Task List»

Colocar una línea de un método en la «Task List» tiene la ventaja que ahora para abrir esa clase, abrir ese método, y ubicarnos en esa línea lo podemos hacer usando el menú del Visual FoxPro.

Captura 3. Si hacemos clic en la opción «Task List» entonces se abrirá la ventana de la lista de tareas
Captura 4. La lista de tareas

Y si ahora hacemos doble clic sobre una línea que habíamos agregado a la «Task List», entonces…

Captura 5. Al hacer doble clic entonces se abre la clase visual (un formulario, por ejemplo), el método, y se ubica el cursor en la línea que habíamos marcado.

Muchas veces cuando estamos programando lo que hacemos es:

  1. Escribimos código en un método
  2. Grabamos el formulario (o la clase visual)
  3. Ejecutamos la aplicación
  4. Verificamos que lo que escribimos en el método está ok
  5. Salimos de la aplicación
  6. Si hay algo mal entonces abrimos el formulario (o la clase visual)
  7. Buscamos el método con el cual estábamos trabajando
  8. Buscamos una línea en ese método
  9. Y corregimos algo

Podemos ahorrar tiempo si en lugar de los pasos 6, 7, y 8 usamos la «Task List» como se mostró en la Captura 3. y en la Captura 4.

Para eliminar una línea de la «Task List» buscamos esa línea en el método en el cual se encuentra, presionamos el botón derecho del mouse para que aparezca el menú contextual y elegimos la opción «Remove Task List Shortcut».

Captura 6. Para eliminar una línea de la «Task List»
Captura 7. También se puede eliminar una tarea directamente desde la ventana de tareas

Recordarnos las tareas que debemos realizar

Además de servirnos para acceder muy rápido a la línea de código de la clase visual y del método que nos interesa la «Task List» también sirve para recordarnos las tareas que debemos realizar aún cuando dichas tareas no estén relacionadas con la programación de computadoras.

Captura 8. En el campo «Contents» podemos escribir cualquier texto y si queremos también podemos escribir una fecha de vencimiento (es opcional)

En el campo «Contents» podemos escribir cualquier texto, en el campo «Due Date» podemos escribir la fecha de vencimiento o la fecha en la cual debemos realizar esa tarea. Es opcional. Pero si escribes una fecha deberá estar con el formato yanqui de MM/DD/AA.

Captura 9. Si ya pasó la fecha de vencimiento entonces esa línea se mostrará en color rojo

Señales visuales

Para ayudarte a identificar las tareas el Visual FoxPro tiene algunas señales:

  • Color rojo. Ya pasó la fecha de vencimiento (Como estoy escribiendo este artículo en fecha 31 de julio de 2022 entonces la tarea de enviar e-mails ya debí haberla realizado, es tiempo pasado, y por lo tanto esa línea es mostrada con color rojo)
  • Color negro. Aún no llegó la fecha de vencimiento (felicitar por el cumpleaños aún está vigente y por lo tanto es mostrada con color negro)
  • Color azul. Hoy es la fecha del vencimiento
  • Negritas. La tarea aún no ha sido leída
  • Texto tachado. La tarea ya ha sido completada
  • Flecha arriba roja. Es una tarea de alta prioridad
  • Flecha abajo azul. Es una tarea de baja prioridad
  • Icono con flecha. La tarea es un atajo para acceder a un método
  • Icono con persona. Es una tarea que tú has definido

Creando tu propia tabla de Lista de Tareas

Si estás desarrollando una única aplicación entonces todas las tareas que tengas en tu lista se referirán a esa aplicación y todo será muy simple, fácil, y sencillo. Pero…¿y si estás desarrollando varias aplicaciones al mismo tiempo? Entonces se te podría complicar la vida porque algunas tareas serán para una aplicación y otras tareas serán para otra aplicación. Un menjunje.

¿La solución?

Tener una lista de tareas independiente por cada aplicación.

Listado 1. Creando una lista de tareas independiente

CLOSE ALL
_FOXTASK = ""
USE HOME(7) + "FOXTASK"
COPY STRUCTURE TO TASKLIST_SUELDOS
INSERT INTO TASKLIST_SUELDOS SELECT * FROM HOME(7) + "FOXTASK"
CLOSE ALL
_FOXTASK = SYS(5) + CURDIR() + "TASKLIST_SUELDOS"

Lo que se hizo en el Listado 1. fue crear una tabla con la misma estructura que tiene la tabla usada en la «Task List». A esa tabla la llamé TASKLIST_SUELDOS pero por supuesto que tú puedes llamarla como prefieras. La tabla original tiene una única fila si no se le agregaron datos, esa única fila fue insertada en TASKLIST_SUELDOS. Si no se hace así entonces TASKLIST_SUELDOS no podrá usarse para las tareas. Finalmente se le dijo que desde ahora en adelante la tabla que se usará para las tareas será TASKLIST_SUELDOS.

Y así como se creó a TASKLIST_SUELDOS se puede crear a TASKLIST_CONTABILIDAD, TASKLIST_FACTURACION, TASKLIST_COLEGIOS, TASKLIST_MEDICOS, TASKLIST_IMPUESTOS, etc. Para cada una de tus aplicaciones puedes tener una «Task List» diferente para que no se mezclen las tareas de una aplicación con las tareas de otra aplicación.

Para cambiar de una «Task List» a otra «Task List» escribes:

Listado 2. Asignando la «Task List» que estará vigente desde ahora

CLOSE ALL

_FOXTASK = ""
_FOXTASK = "D:\VS_SISTEMAS\VS_SUELDOS\TASKLIST_SUELDOS"

De esta manera cada aplicación puede tener su propia «Task List».

NOTA: No siempre es necesario escribir CLOSE ALL y _FOXTASK = «» pero a veces sí, por eso fue mostrado de esa forma.

Listado 3. Abriendo la tabla de la «Task List»

_FOXTASK = ""
USE HOME(7) + "FOXTASK"
BROWSE

Como ya habrás deducido del Listado 1. las tareas se guardan en una tabla .DBF y desde luego que puedes abrir esa tabla y realizar en ella cualquier operación que desees por ejemplo ver su contenido con el comando BROWSE.

Listado 4. Ver el nombre de la tabla de «Task List» actual

? _FOXTASK

La variable interna del Visual FoxPro llamada _FOXTASK guarda el nombre de la tabla de «Task List» que se encuentra vigente en este momento. Inicialmente su nombre es FOXTASK.DBF y se encuentra en la carpeta HOME(7) pero ya sabes como cambiarla si es que quieres usar otra tabla para tus tareas.

Conclusión

La «Task List» o lista de tareas sirve para que ahorremos mucho tiempo cuando trabajamos con métodos de clases visuales (ejemplo: formularios) porque podemos acceder a esos métodos muy rápidamente cuando lo necesitamos.

Adicionalmente también podemos agregar tareas que no están relacionadas con la programación de computadoras.

Hay varios puntos más sobre las «Task List» que no fueron tocados en este artículo y que si eres curioso los encontrarás en la ayuda del Visual FoxPro.

Artículos relacionados

El índice del blog VFPavanzado

Selección múltiple de archivos

Quizás alguna vez necesites que el usuario pueda seleccionar varios archivos al mismo tiempo. Podrían ser varias imágenes, varios archivos PDF, varias planillas Excel, etc.

Las funciones que el Visual FoxPro nos provee para seleccionar archivos: GETFILE() y GETPICT(), no disponen de esa característica porque dichas funciones te permiten seleccionar un solo archivo y te devuelven como resultado el nombre del archivo seleccionado. Eso está muy bien si solamente necesitas seleccionar un archivo pero si necesitas seleccionar más de un archivo entonces no te servirán.

Pero tenemos formas de seleccionar varios archivos y en este artículo veremos una de ellas.

La idea es insertar en nuestro formulario un control «CommonDialog», encontrarás ese control en la biblioteca de clases _SYSTEM.VCX que está en la carpeta \FFC de tu instalación del Visual FoxPro.

Ese control tiene una propiedad llamada lAllowMultiSelect que es la que realiza la magia.

Para que sea fácil conocer las rutas y los nombres de los archivos seleccionados por el usuario creé un cursor que tiene un solo campo/columna y entonces cada nombre de archivo seleccionado insertará una fila/registro en ese cursor.

Si el cursor tiene 3 registros, eso significa que el usuario seleccionó 3 archivos. Si tiene 7 registros es porque seleccionó 7 archivos, etc.

Captura 1. El usuario puede seleccionar varios archivos al mismo tiempo
Captura 2. Los nombres de los archivos seleccionados se guardan en un cursor

Los nombres de los archivos seleccionados por el usuario se guardan en un cursor para que así sea muy fácil conocer cuales archivos seleccionó.

Listado 1. El método INIT() del formulario

  CREATE CURSOR MIS_ARCHIVOS ( ;
    ARC_NOMBRE C(254) ;
  )
  
RETURN
*
*

En el método INIT() del formulario lo que se hizo fue crear el cursor en el cual se guardarán los nombres de los archivos. Ese cursor tiene una sola columna (campo) porque no necesita más.

Listado 2. El método CLICK() del botón

LOCAL lnI
  
  #DEFINE MSG_ICONO_ERROR 16
  
  SELECT MIS_ARCHIVOS
  
  ZAP
  
  WITH ThisForm.SeleccionMultiple
    .ClearFilters()
    .lAllowMultiSelect = .T.                                        && Permite la selección múltiple de archivos
    .cFileName         = ""                                         && Sin un nombre inicial de archivo
    .cInitialDirectory = SYS(5) + CURDIR()                          && La carpeta inicial donde se buscarán los archivos
    .cTitlebarText     = "Selección de varios archivos"             && El texto que se mostrará en la barra de títulos
    .aFilterList[1, 1] = "Archivos de imagen (bmp,gif,jpg,png)"     && Texto que verá el usuario
    .aFilterList[1, 2] = "*.bmp;*.gif;*.jpg;*.jpeg;*.png"           && Las extensiones de archivos que serán mostradas
    .nFileCount        =  0                                         && La cantidad de archivos elegidos por el usuario
    .ShowDialog()                                                   && Muestra la caja de diálogo que le permite al usuario seleccionar varios archivos
    IF .nFileCount > 0 THEN                                         && Si el usuario seleccionó al menos un archivo
      FOR lnI = 1 TO ALEN(.aFileNames, 1)                           && Se recorre el array que contiene los nombres de los archivos
        INSERT INTO MIS_ARCHIVOS (ARC_NOMBRE) VALUES (ADDBS(.cFilepath) + .aFileNames[1, lnI])     && Se agrega la ruta y el nombre del archivo al cursor
      ENDFOR
      BROWSE
    ELSE
      =MESSAGEBOX("Ningún archivo ha sido seleccionado", MSG_ICONO_ERROR, "Ningún archivo seleccionado")
    ENDIF
  ENDWITH
  
RETURN
*
*

En el Listado 2. puedes ver lo que se escribió en el método CLICK() del botón. Lo que hace es asignarle valores a algunas propiedades del control «CommonDialog» y ejecutar su método ShowDialog() el cual guardará en el array aFileNames[] los nombres de los archivos seleccionados por el usuario.

Conclusión

A veces podrías necesitar que el usuario seleccione varios archivos de una sola vez, aquí puedes ver como conseguirlo.

Además de guardar los nombres de esos archivos en un cursor también podrías guardarlos en un ComboBox, en un ListBox, en un EditBox, etc. En este ejemplo se guardaron en un cursor para que te sea fácil verlos.

Descargas

Puedes descargar un archivo .ZIP que contiene el formulario mostrado en este artículo desde:

https://www.mediafire.com/file/zgn70r8tlsz3zh1/MULTI-SELECT.zip/file

Artículos relacionados

El índice del blog VFPavanzado