Arrastrar y soltar con OLE en VFP

From codeWiki
Jump to: navigation, search

Por: VictorEspina

Contents

Introducción

Una de los mejoras cosas que vinieron con la versión 6.0 de Visual FoxPro fué el soporte para Drag & Drop OLE. Cualquiera que haya intentado programar operaciones de arrastrar y soltar con la versión 5.0 sabrá que parecía más fácil de lo que realmente era. Con el nuevo soporte para arrastrar y soltar OLE no solo se simplificó el proceso en general sino que se amplió considerablemente sus posibilidades al hacer que las aplicaciones VFP puedan interactuar con cualquier otra aplicación compatible con esta técnica (Windows, controles ActiveX, etc).

Lo primero que debemos tener claro a la hora de implementar la técnica de arrastrar y soltar OLE en VFP (que llamaré OLEDD en lo sucesivo) es lo siguiente:

  • Existe un objeto que se arrastra, el cual conoceremos como el orígen de la operación de arrastre.
  • Algunos objetos aceptan que otros objetos sean arrastrados y soltados encima de ellos; estos objetos los conoceremos como destinos de la operación de arrastre.
  • Existen varias acciones posibles al momento de arrastrar un objeto y soltarlo en otra parte; podemos moverlo, copiarlo o establecer un vínculo entre el origen y el destino de la operación de arrastre.
  • Todo objeto origen contiene un objeto llamado DataObject el cual se crea al momento de empezar la operación de arrastre, y contiene uno más datos en diferentes formatos. Algunos de estos datos son creados automáticamente por VFP al momento de arrastrar el objeto y otros podemos crearlos nosotros mismos.
  • Ningún objeto destino aceptará a un objeto origen que esté siendo arrastrado sobre él, a menos que nosotros le digamos que dicho objeto origen contiene datos que pueden ser manejados por el destino.


Configurando el objeto origen

La mejor forma de explicar el funcionamiento de esta técnica es mediante un ejemplo. Lo primero que haremos será crear un formulario de ejemplo. Dentro de este formulario, creamos un objeto Image y le asignamos una imágen cualquiera. Lo que haremos será configurar tanto el objeto Image (el cual será nuestro objeto origen) como el formulario (nuestro objeto destino) para que podamos mover la imágen a cualquier punto en el formulario.

Lo primero que tenemos que hacer para configurar nuestro objeto origen es decirle que puede ser movido; esto lo podemos lograr de dos formas:

  • Colocamos en 1 - Automática la propiedad OLEDragMode
  • Dejamos la propiedad OLEDragMode en 0 - Manual y escribimos el siguiente código en el evento MouseMove:
IF nButton=1 && ¿Botón izquierdo pulsado?
 THIS.OLEDrag(.T.) 
ENDIF

El parámetro .T. en el método OLEDrag le indica a VFP que debe ser él el que decida cuantos pixeles debe mover el ratón el usuario con el botón izquierdo pulsado antes de iniciar la operación de arrastre.

Finalmente, si queremos utilizar algún cursor especial para que se muestre cuando estemos arrastrando nuestro objeto origen, configuramos la propiedad OLEDragPicture. Lo interesante de esta propiedad es que no solo acepta los tradicionales archivos .ICO, .CUR o .ANI, sino que ahora acepta BMP, GIf y hasta JPG!.


Configurando el objeto destino

Una vez configurado nuestro objeto origen solo nos queda configurar el formulario, nuestro objeto destino, para decirle que puede aceptar objetos soltados en él. Por omisión, ningún objeto funciona como destino de una operación OLEDD, nosotros debemos configurar específicamente aquellos que estarán en capacidad de manejar esta clase de operación. Esto es una gran ventaja con respecto a la versión anterior, ya que antes habia que configurar cada objeto por donde podia pasar un objeto origen para actualizar el cursor de arrastre de modo que visualmente se indicara que allí no podia ser soltado dicho objeto.

Ahora bién, todo objeto puede comportarse de tres formas distintas ante una operación de arrastre:

  • El objeto no es un destino de arrastre (OLEDropMode = 0 - Desactivado)
  • El objeto es un destino de arrastre (OLEDropMode = 1 - Activado)
  • El objeto pasará la operación de arrastre a su contenedor (OLEDropMode = 2 - Pasar al contenedor)


Siguiendo con nuestro ejemplo, debemos configurar la propiedad OLEDropMode de nuestro formulario a 1 - Activado para indicarle que será un destino de arrastre. Ahora. ¿significa esto que el formulario puede manejar cualquier objeto que sea soltado en él? no, lo único que significa es que el formulario aceptará que algunos objetos sean soltados en él. De hecho, si ejecutamos ahora nuestro formulario de prueba y probamos a arrastrar el objeto Image, veremos que visualmente se nos indica que no podemos soltarlo sobre el formulario.

Para que el formulario determine si puede manejar el objeto que está siendo arrastrado sobre él, debemos utilizar el objeto DataObject del objeto origen. Tal como indicamos al principio, este objeto DataObject contiene uno o más datos en diferentes formatos relativos al objeto que está siendo arrastrado y métodos que nos permiten interrogarlo acerca de su contenido.

Para hacer que nuestro formulario interrogue al objeto que está siendo movido sobre él, debemos programar el evento OLEDragOver del formulario, el cual se dispara cada vez que arrastramos un objeto origen sobre un objeto destino:

IF oDataObject.GetFormat(CFSTR_VFPSOURCEOBJECT)
  THISFORM.OLEDropHasData=DROPHASDATA_USEFUL
ENDIF

Las constantes CFSTR_VFPSOURCEOBJECT y DROPHASDATA_USEFUL están definidas en FOXPRO.H. Expliquemos ahora el código paso a paso:

 
IF oDataObject.GetFormat(CFSTR_VFPSOURCEOBJECT)

Preguntamos al objeto DataObject si el objeto origen contiene datos para el formato CFSTR_VFPSOURCEOBJECT. Este formato representa a objetos VFP y está disponible cada vez que arrastramos un objeto VFP. El contenido asociado a este formato no es más que una referencia al objeto que estamos moviendo.

THISFORM.OLEDropHasData=DROPHASDATA_USEFUL

Le indicamos al objeto destino (es decir, nuestro formulario) que el objeto que está siendo arrastrado sobre él, contiene datos que él puede manejar. Cuando hacemos esto, VFP cambia el icono de arrastre para indicar que el objeto puede ser soltado allí.

Finalmente, lo único que nos falta por hacer es decirle al formulario que debe hacer cuando se suelte un objeto origen sobre él. Esto lo logramos programando el evento OLEDragDrop del formulario, el cual se dispara cuando soltamos el objeto origen sobre el objeto destino:

LOCAL oObj
oObj=oDataObject.GetData(CFSTR_VFPSOURCEOBJECT)
oObj.Move(nXCoord,nYCoord)

El método GetData del objeto DataObject me permite obtener la data asociada a un formato específico en el objeto origen. En este caso, tal como mencionamos anteriormente, la data asociada al formato CFSTR_VFPSOURCEOBJECT no es más que una referencia al objeto VFP que se está arrastrando; una vez obtenida la referencia al objeto origen, todo lo que debemos hacer es mover nuestro objeto origen a su nueva posición.

¡ Y esto es todo ! sencillo, ¿no?. Ahora, exploremos un poco más este asunto de los formatos y los datos en el objeto DataObject.


Más sobre el objeto DataObject

Una de las cosas que más me gustó sobre esta nueva técnica OLEDD es el uso del objeto DataObject. Cada objeto VFP que se arrastra contiene 1 o más datos en su objeto DataObject que permiten al destino manejar fácilmente la operación de arrastre. A continuación mostraremos una lista de los formatos disponibles para los objetos más comunes:

Objeto

Formato

Data

TextBox, ComboBox, ListBox,CheckBox CF_TEXT Value o Caption del objeto
CFSTR_OLEVARIANT Idem a CF_TEXT
CFSTR_VFPSOURCEOBJECT Referencia al objeto
Label, CommandButton CF_TEXT Caption del objeto
CFSTR_VFPSOURCEOPBJECT Referencia al objeto
Image CFSTR_VFPSOURCEOBJECT Referencia al objeto

Tip: En la galeria de componentes de VFP6, grupo Ejemplos/Soluciones/VFP 6.0/Arrastrar y colocar OLE hay un ejemplo de nombre 'Consulta arrastrar y colocar OLE' el cual le permitirá saber los diferentes formatos disponibles para cualquier objeto deseado.

Pero, ¿que pasa si queremos arrastrar objetos de la misma clase pero con funcionalidad diferente? pongamos un ejemplo para ilustrar la situación: supongamos que tenemos un objeto listbox que contiene una lista de ciudades disponibles, las cuales podemos arrastrar y soltar en otro objeto listbox que contiene las ciudades seleccionadas; el usuario solo debe arrastrar un elemento del listbox A al listbox B para seleccionar una ciudad. Ahora bién, supongamos también que tenemos otro par de objetos listbox (C y D) con la lista de paises disponibles y seleccionados respectivamente:

OLEDD1.jpg

Tal como podemos observar en la imágen, el usuario puede mover elementos de A->B, A<-B, C->D, C<-D pero no de A->C o C->D. Todos los objetos listbox son objetos destino pero solo deben aceptar objetos origen con unas ciertas características. La elección más evidente es usar la clase base del objeto:

IF oDataObject.GetFormat(CFSTR_VFPSOURCEOBJECT)
 LOCAL oObj
 oObj=oDataObject.GetData(CFSTR_VFPSOURCEOBJECT)
 IF lower(oObj.BaseClass)="listbox"
  THIS.OLEDropHasData=DROPDATA_USEFUL
 ENDIF
ENDIF

Sin embargo, esto no evita que el usuario pueda hacer un movimiento incorrecto. Podríamos utilizar el nombre del objeto para saber si podemos aceptarlo o no. Supongamos el siguiente código en el evento OLEDragOver del listbox A:

IF oDataObject.GetFormat(CFSTR_VFPSOURCEOBJECT)
 LOCAL oObj
 oObj=oDataObject.GetData(CFSTR_VFPSOURCEOBJECT)
 IF lower(oObj.BaseClass)="listbox" and lower(oObj.Name)="lstB"
  THIS.OLEDropHasData=DROPDATA_USEFUL
 ENDIF
ENDIF

Sin embargo, esto sería poco práctico porque nos obligaria a cambiar el código en los eventos OLEDragOver si alguna vez cambiamos el nombre de los listbox. Una mejor forma es añadir nuestros propios formatos y datos al objeto oDataObject. Una de las cosas que hay que tener en cuenta con esto es que podemos crear un formato nuevo en el DataObject sin que exista algún dato sociado a él; la idea es verificar solo si el formato existe (con el método GetFormat) y no obtener los datos asociados al mismo. Entonces:

  • Para añadir un nuevo formato sin datos asociados al objeto DataObject, utilizamos el método SetFormat()
  • Para añadir un nuevo formato con datos asociados o para establecer los datos asociados a un formato existente, utilizamos el método SetData()


Ahora, existen dos eventos del objeto origen donde es posible utilizar estos métodos:

  • Evento OLEStartDrag: se dispara cuando se inicia el proceso de arrastrar y soltar. Este evento se dispara solo una vez, y puede utilizarse en él tanto el método SetFormat como el SetData.
  • Evento OLESetData: se dispara cuando el objeto destino ejecuta el método GetData y no hay datos en el objeto DataObject para el formato indicado. En este evento se utiliza exclusivamente el método SetData para establecer los datos del formato solicitado. Esta técnica se llama "presentación con retraso" y le permite colocar datos en el DataObject únicamente cuando se solicitan.


Apliquemos esto en el ejemplo que nos ocupa; utilizemos el método SetData para crear un nuevo formato en cada listbox que identifique a que grupo pertenece (ciudades o paises). Para hacer, esto colocamos el siguiente código en el evento OLEStartDrag de los listbox A y B:

oDataObject.SetData("C","GRUPO)

mientras que para los listbox C y D hacemos:

oDataObject.SetData("P","GRUPO)

Una vez hecho esto, programamos de la siguiente forma el evento OLEDragOver de los listbox A y B:

IF oDataObject.GetFormat(CFSTR_VFPSOURCEOBJECT) and oDataObject.GetFormat("GRUPO")
 IF oDataObject.GetData("GRUPO")="C"
  THIS.OLEDropHasData=DROPDATA_USEFUL
 ENDIF
ENDIF

Notemos que el código oDataObject.GetFormat("GRUPO") nos permite asegurarnos que el objeto no solo es un objeto VFP sino que es un listbox de paises o ciudades. Finalmente, oDataObject.GetData("GRUPO")="C" nos permite asegurarnos que el objeto es un listbox de ciudades y no de países.


Conclusiones

El soporte para Arrastrar y Soltar OLE incluido en VFP 6.0 nos permitirá no solo poder manejar operaciones de arrastre entre objetos VFP, ActiveX y el explorador de Windows, sino que su flexibilidad y sencillez hara posible construir aplicaciones más amigables e intuitivas con un menor esfuerzo que con las técnicas tradicionales de Arrastrar y Soltar de VFP.

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox