viernes, 25 de abril de 2008

Usando origenes de datos ADO y 2

Desde febreo no públicaba nada en el blog, y es que el trabajo estos últimos meses ha sido más intenso.
Para esta segunda entrega tenia pensado implemenar al ejemplo que hizcimos en la entrada anterior, autosort por columnas, y busqueda incremental para la descripción.

El tema del autosort, en la versión actual de FiveWin, xBrowse ya lo incluye nativamente, ademas de DATAS para el manejo del orden, con lo que en realidad no nos sera muy util si estamos usando FiveWin actualizado, y será meramente educativo, aunque si estais en versiones antiguas de FiveWin os permitira implementar esa funcionalidad.

Para hacer las ordenacines de campos y las busquedas, utilizaremos el Data Sort y el metodo Find del obbjeto RecordSet. El ámbito, serán los registro que formen parte de nuestra consulta SQL, y tiene como ventaja que no genera trafico con el servidor(con una sentenica SQL podriamos conseguir el mimsmo resultado, pero cargariamos proceso al servidor y generariamos tráfico en la red).

El data Sort ordena uno o varios campos del Recordset de forma ascendente o descendente. Para ello debemos asignar una cadena a esta propiedad con el nombre o nombres de los campos a ordenar, separados por comas e indicar el modo de ordenación mediante las palabras claves ASC o DESC.
oRs:Sort:="CliDes ASC"
Ordenado por el campo CliDes de forma ascendente.
El metodo Find busca un registro en función de un criterio. Si el criterio se cumple, el registro localilzado pasa a ser el actual, en caso contrario el Recordset se sitúa al final.
Su sintaxys:
oRs:Find( cCriterio, SkipRecords, DireccionBusqueda, Start )
En el criterio no puede usarse OR ni AND, y solo puede aparecer uno de los siguientes operardores = > >= < <= <> LIKE .

Pasemos a la acción, vamos a usar practicamente el mismo código que en la entrada anterior, con algunos añadidos.
En la parte donde creamos el fichero MDB, y lo rellenamos con datos de prueba, el campo descripción lo rellenaremos con valores alfabéticos aleatorios para poder probar las busquedas incrementales.
FOR x:=1 TO 20
cCliDes+=Chr(HB_RandomInt(65,70))
NEXT
oRs:Fields("CliDes"):Value:=cCliDes


Para poder realizar la busqueda incremental, necesitamos un objeto SAY, y una funcion que realice la busqueda y nos devuelva verdadero o falso según el resultado de la busqueda.

oBar := TBar():New(oWndVerMdb,50,45,.T.)
@ 01,10 SAY oSay PROMPT '' SIZE 90,20 OF oBar

Creamos una barra, y dentro de la barra colocamos el SAY que visualizara la cadena que estamos buscando.

Y ahora le indicamos al browse el objeto SAY y la funcion que procesara la busqueda.

oBrw:bSeek := { | c | Busca(c,oRs) }
oBrw:oSeek := oSay


Y la función encargada de realizar la busqueda es la siguiente.
Funcion para busqueda
La función recibe dos parámetros, la cadeana a buscar, y el objeto recordset(podriamos mejorar la función y pasarle como parametro el campo sobre el que queremos hacer la busqueda). Montamos la cadena de busqueda dependiendo de si es el primer caracter tecleado o no, y se lo pasamos al recordset. Buscamos en el campo CliDes que empiece por la cadena que recibimos como parametro mas cualquier otra cosa( LIKE "cadean busqueda"+"*").
Si no es inicio o final de fichero significa que hemos encontrado un regitro, y devolvemos verdadero.

Con esto ya tenemos implementada la busqueda incremental en el browse.

Pasemos ahora a añadire la opcion de ordenar los registros cuando pulsamos sobre la cabecera de la columna.

Despuese de la instruccion SetAdo(), añadimos la siguiente linea, donde le indicamos un codeblock a ejecutar cuando se pulsa con el raton en la cabecera.
AEval(oBrw:aCols,{|oCol|oCol:bLClickHeader:={|f,c,h,oCol|Ordena(oBrw,oRs,oCol)}})
Y la fucion encargada de controlar el orden es la siguiente
Funcion para sort
En esta función casi nos lleva mas trabajo el ir colocando el BMP de la flecha arriba abajo, que realizar la ordenación. Si no visualizamos el BMP Ordena quedaria reducido de la siguiente manera:
STATIC FUNCTION Ordena(oBrw,oRs,oCol,cFld)
LOCAL cSort:=oRs:Sort,nLen,nFor
oRs:Sort:=cFld+IF(At(cFld,cSort)>0,IF(At('ASC',cSort)>0,' DESC',' ASC'),' ASC')
oRs:Sort:=cFld+Space(1)+oCol:cOrder
oBrw:GoTop()
RETURN NIL


La funcion VerMdb, con las lineas que hemos añadido para la busqueda y la ordenación de columnas, queda de la siguiente manera:
VerMdb
Desde aqui podeis descargar el código fuente, y el ejecutable.
Descargar ejecutable
Descargar código fuente
Como siempre, espero que os haya resultado amena la lectura, y ademas os pueda ser de utilidad.
Saludos desde Mallorca.
(ADO RecordSet Harbour xHarbour FiveWin oRs.Find oRs.Sort)

lunes, 18 de febrero de 2008

Usando origenes de datos ADO 1

En esta entrada, voy a incluir un ejemplo autocontenido en el que vamos a abrir un origen de datos via ADO, y visualizaremos su contenido en una rejilla (xBrowse).
Para tal efecto, y para que no sea requerimiento tener algun servidor instalado, utilizaremos un fichero MDB (Microsoft Access), dicha base de datos la crearemos dinamicamente desde nuestro código, haciendo uso de los componentes ADOX (Active Data Objects Extension).
Para que fucnione, debemos tener instalado en nuestro ordenador el proveedor de datos Microsoft.Jet.OLEDB.4
Descargar ejecutable
Descargar código fuente

Este programa consta de dos partes, la primera parte donde creamos la base de datos, la tabla, y rellenamos con datos de prueba, que corresponde la siguiente porción de código, la cual no creo que haga falta comentar, puesto que con la lectura y los comentario en el propio código fuente, es autoexplicativo.

Creando la base de datos Demo.mdb
Como podeis ver creamos un catalogo, despues una tabla, añadimos campos a la tabla, definimos la clave primaria, y finalmente añadimos la tabla al catálogo.

Rellenando con datos de prueba
En esta segunda funcion, definimos un objeto connection, creamos un recordset, definimos CursorLocation del lado del cliente (salvo contadas excepciones, siempre los definiremos del lado del cliente), tipo de cursor Dinamico (aqui si que tendremos más juego dependiendo de las operaciones que queramos realizar con los datos), tipo de bloqueo (en este caso optimista), le indicamos cual es el objeto connection que debe usar, y la sentencia SQL a ejecutar.
Una vez abierto, mediante un bucle FOR ... NEXT vamos creando registros de prueba.

Cuando ya tenemos creado nuestra base de datos access, lo único que nos resta es visualizar los registros que le hemos incorporado.
Con muy pocas instrucciones podemos ver en pantalla todos los registros, en esta primera entrega, solo visualizaremos los registros no haremos ninguna funcion de mantenimiento, si bien en la fucnion CrtTstDat, ya podeis ver como funciona la mecánica para añadir registros.

En la función VerMdb, recuperamos todos los datos contenidos en la tabla Cliente, y los visualizamos en un Browse. Con solo diez lineas es suficiente, y eso en parte es gracias al uso del metodo SetAdo(aportación personal donada a FiveTech) de la clase xBrowse que nos crea las columnas y nos configura los codeblock de movimiento (bGoTop, bGoBottom, bSkip, ... )

Visualizar Tabla en rejilla
Espero que os sea de utilidad, espero vuestros comentarios.
Saludos.

(ADOX:Catalog ADOX:Table ADOX:Key ADODDB:Connection ADODB:RecordSet tXBrowse SetAdo )

jueves, 17 de enero de 2008

TreeView con xBrowse

Aunque muchas aplicaciones usan la visualizacion tipo arbol, la verdad es que hasta la fecha nunca lo habia usado en mis programas.
Esta vez tenia muy claro el resultado que queria obtener, pero ni idea de como llegar hasta el.
Queria visualizar un plan de cuentas, con una estructra arbolada a la izquierda y una rejilla con las cuentas a la derecha.
Tree plan de cuentas

Este es el resultado final, y es justo como deseaba que quedase ;-).
La tabla cta esta formada por los siguientes atributos:
CTA Caracter 8
NOM Caracter 45
Image Hosted by ImageShack.us
Y los registros están grabados tal cual se ve en la imagen superior.

Para conseguir nuestro proposito usaremos un splitter vertical, colocando un objeto TreeView a la izquierda y un xBrowse a la derecha.
Creamos el objeto oTree del tipo TreeView
oTree:=TTreeView():New(0,0,oWnd,,,,,300)
y lo procedmos a rellenearlo con los datos que deseamos, para ello llamos a la función AddMayor, pasandole como parametro el propio objeto oTree, y un array vacio(paso el array desde esta funcion para poder darle algun tipo de uso futuro, ahora mismo no lo uso para nada, y podriamos prescindir de pasar el arraym y crearlo como una variable local en la función AddMayor).
Image Hosted by ImageShack.us

En esta función uso un indice condicional 'mayor', que solo contiene las cuentas de mayor y no las subcuentas.
Lo que hago es recorrer todo el fichero, e ir agregando nodos al objeto oTree, la forma de agregar varia si estamos en el nodo raiz, o añadimos a uno ya existente.
Si la longitud del contenido del campo cta es 1, estamos en el nodo raiz, añadimos el elemento con el metodo Add del objeto oTree, que devuelve otro objeto del tipo tvItem.
oTree:Add(AllTrim(Cta->Cta)+' '+Cta->Nom)
Mientras que si no estamos en el nodo raiz(long. cta>1), lo que hacemos es utilizar el metodo Add, pero del objeto tvItem correspondiente.
Buscamos en el array el item que corresponde al valor de cta actaul menos una posicion, y si le añadimos el nuevo elemento con la siguiente instrucción.
aItems[nPos,2]:Add(AllTrim(Cta->Cta)+' '+Cta->Nom,,AllTrim(Cta->Cta))

A medida que voy creando los nodos, guardo el objeto tvItem y el valor de Cta en un array. Este array lo utilizo para posicionarme en el nodo correcto mediante la funcion Ascan.

Si os fijais en cada elemento de cada nodo del objeto tree es un objeto tvItem, el objeto tvItem dispone de un data llamado cargo (presente en muchos otros objetos) que esta diseñado para guardar cualquier dato que quiera el programador, en nuestro caso, grabamos el valor del campo Cta, y nos va a servir para hacer un Scope en el browse, a medida que nos vayamos desplazando por el arbol.
Para conseguirlo usamos el data bChanged, en el cual colocamos un codeblock que sera evaluado cada vez que haya un cambio en el objeto oTree,


CodeBlock bChanged
Este CodeBlock, recupera el elemento seleccionado del oTree, comprueba que no este vacio el Data Cargo, aplica el scope a la tabla, reposiciona y refresca el objeto browse.

El resto de trabajo que nos queda es definer el browse y el spliter, que supongo que todos ya sabreis perfectamente como hacerlo.

Image Hosted by ImageShack.us
Es importante el data nLeft del objeto browse para que quede a la derecha del splitter, y el codeblock bResized de la ventana, para que se reajuste el splitter si cambia el tamaño de la venana.

Espero que os haya gustado la lectura, y os pueda ser de utilidad, todos los comentarios son bienvenidos.

(tTreeView tVItem tXBrowse oTree:bChanged SetRDD )

lunes, 19 de noviembre de 2007

tDataBase, usando tablas como objetos.


Puede que pienses que esto no tiene nada que ver con ADO, y en efecto directamente no tiene nada que ver con ADO. En una entrada anterior comentamos que para trabajar con ADO, era necesario conocer algo de SQL, lo que no compentamos es que tambien es necesario manejarse bien con la programación orientada a objetos (POO).


En FiveWin está incluida un clase que nos permite manjear las tablas DBF como objetos, eso nos permitiira por una parte probar y tomar soltura en el manejo de propiedades, metodos y ademas es una forma muy eficaz de trabajar.


Esta clase nos controla los bloqueos de registro, incorpora un buffer para no modificar directamente sobre el registro, controla si hay cambios en los datos editados, y muchas mas cosas, que nos llevaria bastante trabajo programar a mano.

Su uso es muy sencillo, y ahora veremos un pequeño ejemplo.


El primer paso, es crear el objeto (instanciar de la clase), abriremos una tabla, por ejemplo de proveedores

DBUseArea(.T.,'DBFCDX','PROVEEDOR','PROVEEDOR,.T.,.F.)

oDbf := TDataBase():New()

ya tenemos creado y funcional nuestro objeto oDbf, disponemos de todos los metodos necesarios para trabajar con una tabla (oDbf:Skip(), oDbf:GoTop(), oDbf:Save(), oDbf:Used(), ...), en la ayuda de fivewin encontrareis todos los metodos disponibles, y en la carpeta Source\clases, podeis consultar el código fuente y ver como funcina internamente.


Siguiendo con un proceso normal de altas y modificaciones de ficheros, el siguiente paso seria determinar si queremos añadir un registro, o editar el registro actual.

Si quermos hacer una alta, invocaremos el metodo Blank, que nos dejara el buffer de edición vacio y listo para ñadir un nuevo registro.

Por el contrario si queremos editar el registro actual, invocaremos el metodo Load, que cargara el contenido de los campos de la tabla, en el buffer de edición.


Acto seguido ya podemos visualizar, editar y modificar el contenido de los campos de nuestro objeto, y la forma de referirnos a los campos es nombre de nuestro objeto : nombre del campo.

@ 10,10 GET oDbf:Codigo

@ 12,10 GET oDbf:Nombre ....

cuando hayamos introducido todos nuestros datos, si queremos guardarlos actuaremos de la siguiente manera.

Comprobaremos si realmente se han modificado los datos, y para ello consultamos el valor de oDbf:Modified(), si nos devuelve verdadero, es que hay cambios.


Si el registro es una alta, en este momento crearemos el registro (hasta ahora hemos estado trabajando sobre un buffer temporal en memoria).

oDbf:Append()

y tanto si es una alta como una edición de un registro existente, grabaremos el contenido del buffer temporal a los campos de la tabla.

oDbf:Save()


Sencillo y potente, verdad?. Bueno pues ADO, funciona de una manera muy parecida, cambian los nombres de algunos metodos, pero en el fondo la filosofia de funcionamiento es la misma.


Aqui teneis algunos metodos con la misma funcionalidad tanto en tDatabase como en ADO


BielSys











Código fuente ejemplo mantenimiento ficha de proveedores usando tDatabase:
STATIC FUNCTION EdtPro(lAdd)

   LOCAL oDlg,oDbf,lSave:=.F.

   DEFAULT lAdd:=.F.

   Select Proveedores

   DATABASE oDbf

   DEFINE DIALOG oDlg RESOURCE "MANPROVE"



   IF lAdd

      oDbf:Blank()

   ELSE

      oDbf:Load()

   ENDIF

   oDlg:SetText('('+ProcName()+')'+IF(lAdd,' Añadir Proveedores',' Editar ficha proveedor'))



   REDEFINE GET oDbf:Codprov   ID 601 OF oDlg VALID {|oGet|Ajusta(oGet)} WHEN lAdd

   REDEFINE GET oDbf:Nombre    ID 602 OF oDlg

   REDEFINE GET oDbf:Nif       ID 603 OF oDlg

   REDEFINE GET oDbf:Dir1      ID 604 OF oDlg

   REDEFINE GET oDbf:Dir2      ID 605 OF oDlg

   REDEFINE GET oDbf:Cp        ID 606 OF oDlg

   REDEFINE GET oDbf:Provincia ID 607 OF oDlg

   REDEFINE GET oDbf:Tel1      ID 608 OF oDlg

   REDEFINE GET oDbf:Tel2      ID 609 OF oDlg

   REDEFINE GET oDbf:Movil     ID 610 OF oDlg

   REDEFINE GET oDbf:Fax       ID 611 OF oDlg

   REDEFINE GET oDbf:Percon    ID 612 OF oDlg



   REDEFINE BUTTON ID 1 OF oDlg ACTION (oDlg:End(), lSave := .T.) // OK

   REDEFINE BUTTON ID 2 OF oDlg ACTION (oDlg:End()) // Cancel



   ACTIVATE DIALOG oDlg CENTERED



   IF lSave .AND. oDbf:Modified()

      IF lAdd

         oDbf:Append()

      ENDIF

      oDbf:Save()

   ENDIF

RETURN NIL


_________________
Saludos desde Mallorca

Biel

Gabriel Maimó

miércoles, 7 de noviembre de 2007

ADO ConnectionString

Al usar ADO, el primer escollo con el que nos vamos a encontrar, es el ConnectionString. Es la
cadena que tenemos que pasarle como parametro al objeto connection, para que se conecte con nuestro servidor de base de datos. Si fallamos en este paso, es imposible que nada nos funcione, por eso es un parametro muy importante, y que debemos especificar correctamente. A tal efecto disponemos de una web en la cual podemos encontrar las cadenas de conexión de la mayoira de servidores http://www.connectionstrings.com/ Pero tambien diposnemos de otro medio más sencillo y practico con un pequeño programa desarrollado por nosotros mismos, y explotando automatizacion ole y el componente DataLinks. La gran ventaja es que podreis probar al momento si la conexión con el servidor se realiza correctamente.

Aqui teneis el codiggo fuente, como veis son cuatro linea.

#include "FiveWin.Ch"
FUNCTION main()
LOCAL oDataLink := TOleAuto():New("Datalinks"),;
oConn := oDataLink:PromptNew(),;
cConn:=''
cConn:=oConn:ConnectionString
?cConnRETURN
RETURN NIL
FUNCTION RddSys(); RETURN NIL

Al ejecutar nos aparece la siguiente pantalla
.Image Hosted by ImageShack.us

Como podeis ver tenemos cuatro pestañas, en la primera nos aparecen todos los
controladores OLE DB que tenemos instalados en nuestro sistema.


En la segunda, nos permite seleccionar un acceso a datos via ODBC,
previamente debe de haberse definido el origen de datos en panel de
control, herramientas administrativas.


Image Hosted by ImageShack.us


La pestaña dos varia si elejimos el acceso via OLE DB, presentara el siguiente
aspecto.

En esta pantalla debemos especificar, el origen de datos, ubicacion, usuario y contraseña, si procede.

Image Hosted by ImageShack.us


Ademas en esta pantalla, disponemos de un boton que nos permite probar la conexion. Si la prueba de conexion es correcta, nos devuelve una ventana con un mensaje de confirmación.

Image Hosted by ImageShack.us


Disponemos de dos pestañas adicionales, donde pueden definirse otros parametros, y en la última pestaña donde nos visualiza todos los parametros definidos para la conexón.

Si pulsamos aceptar, nos aparecera otra ventan visualizando el contenido de la variable cConn,
que es el string que debemos pasar al objeto connection. De esta manera podras definir un connectionString, con todos los parametros necesarios, para el origen de datos que quieras, sin preocuparte por la sintaxis.

Image Hosted by ImageShack.us


Bueno, como siempre espero que os sea de utilidad, y que sigais leyendo el blog de vez en cuando. Un saludo desde Mallorca.

viernes, 2 de noviembre de 2007

CONEXION A ORIGENES DE DATOS SQL

Al comentzar a escribir este blog, me han llegado sugerencias de que escriba sobre el tema ADO, SQL, etc, pues parece es algo que cada dia interesa más a la comunidad xBase.

En este primera entrada referente al tema SQL, comentare brevente tres de las opciones que disponemos los usuarios xBase(FiveWin) para conectarlos a bases de datos SQL.

  1. ODBC (Open Data Base Conectivity). Es el sistema más antiguo con el que contamos, y actualmente en el foro cuenta con algo de mala fama.
    Personalmente lo he utilizado, y he conseguido muy buenos resultados. Toda la funcionalidad esta descrita en dos clases de fiveWin, de las cuales disponeis del código fuente. Eso permite el control absoluto del funcionamiento, y ademas la adecuación en caso de necesidad. Una pega en cuanto a seguridad, es que trabaja con ficheros temporales DBF que los crea al vuelo, y carga con los datos obtenidos del servidor. Para conectarnos al servidor de bases de datos, debemos crear un origen de datos ODBC(DSN de sistema normalmente), puede ser creado por programa, aunque no es algo trivial. Algunos instaladores tambien permiten crear origenes de datos ODBC.

  2. ADO (ActiveX Data Objects), posiblemente este sea el metodo mas potente del que disponemos para atacar a motores de bases de datos, aunque tambien el que va a requerir más conocimientos. En la red podremos encontrar infinidad de documentacion, sobre los objetos ado, sus metodos, propiedades, etc. Para conectarlos al servidor, tenemos que definir una cadena de conexión dentro de nuestro programa, no es necesario definir nada externo a nuestro programa. Para conexion con ADO necesitamos que el motor de base de datos soporte OLE DB, aunque si no lo soporta, simpre podemos acceder mediante un origen de datos ODBC ya definido.

  3. AdoRdd,se trata de manejar conexiones via ADO, pero con comandos xBase. Es la última opción que ha aparecido, y es sin duda la mas sencilla de implentar y casi no requiere conocimiento alguno de ADO. De haber existido antes, seguramente es la que hubiera escojido para mis proyectos. Quizas no se llegue al control o rendimiento que se pueda
    alcanzar utilizando ADO directamente, pero el coste de implementación y aprencizaje, es mucisimo menor.

Con cualquiera de las tres opciones, es casi indispensaple unos conocimientos minimos del lenguaje de consulta de datos SQL, y del funcionamiento y diseño de las bases de datos relacionales. Cuanto mas conozcas SQL, mucho mejor.

jueves, 11 de octubre de 2007

Usando List & Label 12

Recientemente en el foro de FiveWin se viene hablando del generador de informes List & Label. Y aunque se comenta que es muy bueno, que funciona muy bioen, etc, la verdad es que no hay hasta la fecha ningún ejemplo funcional para ser usado con FiveWin. Bueno realmente si hay una primera aproximación de Jairo Centeno (gracias), que expone un ejemplo de como usar el generador de informes.

En esta entrada intentare poner un ejemplo de como usar el diseñador, y otro de lanzar un informe.

El primer punto a destacar es la filosofía de uso de este generador de informes. Mientras que la mayoria de generadores se basan en la conexion a un origen de datos, LL recibe los datos al momento, y su origen puede ser cualquiera, dejando en manos del programador el envio de datos al reporte.

La función DefineData, es la encargada de pasar los datos al reporte, tanto para diseño como para impresión. El primer paso es convertir los datos xBase a formato LL, y el segundo definir el campo o la variable dependiendo del valor recibido como parametro en lAsField. El segundo parametro oLl, es un objeto valido LL ya creado.

Para lanzar el diseñador de informes, llamamos a la funcion Disenya los pasos a seguir son muy simples.
1.- Creamos un nuevo objeto LL oLl = TActiveX():New( oWnd, "L12.List-Label12_Ctrl_32.1" )
2.- Ajustamos varias propiedades del objeto LL
3.- Llamamos a la función DefineData, que le enviara los campos al diseñador de informes.
alias->( DefineData(.t., oLl) )
4.-Invocamos el diseñador
oLl:Do("LlDefineLayout",oWnd:hWnd,"Designer",LL_PROJECT_LIST, "informe.lst")
Facil y sencillo, ahora manipulamos y diseñamos nuestro informe, y lo guardamos. Ya tenemos listo un informe para ser llamado desde nuestra aplicación.

Ya tenemos un informe creado, ahora vamos a llamar el informe desde nuestro programa, podeis revisar el código de la función Imprime que teneis más abjo, el funcionamiento es el siguiente:
1.- Creamos un nuevo objeto LL oLl = TActiveX():New( oWnd, "L12.List-Label12_Ctrl_32.1" )
2.- Ajustamos varias propiedades del objeto LL (inicio de variables, de campos, immpresora por defecto, ...)
3.- Llamamos a la función DefineData, que le enviara los campos al diseñador de informes.

Hasta aqui igual que para el diseñador.

4.- Empezamos la impresión

oLl:Do("LlPrintStart",LL_PROJECT_LIST,"Informe.lst",LL_PRINT_PREVIEW)
oLl:DO("llPrint")

5.- Montamos un bucle que recorra los registros que queremos imprimir, y para cada registro llamamos la Funcion DefineData (convierte y envia los datos aLL) y oLl:DO("llPrintFields") que imprime los campos.

5.- Una vez que hemos terminado los registros que queriamos imprimir, solo nos queda indicarle a LL que ya esta finalizado el listado.
oll:DO("llPrintEnd",0)
oLl:DO("LlPreviewDisplay", "informe.lst", , oWnd:hWnd)



Esto es un ejemplo muy sencillo, pero espero os aclare un poco el modo de funcionar, para ver con más profundidad todas las funciones y parametros, revisad la ayuda del producto.
En el código fuente incluido, se utiliza una tabla cuyo alias es tmp, y se presupone abierta, y una ventana oWnd tambien se presupone definida, y con scope en las funciones.
En la transformacion de tipos de datos, el formato fecha lo convierto a texto, aunque podria convertirse tambien a formato juliano si quisieramos hacer algún tipo de operación con la fecha dentro del informe.
Nada más, esta es mi primera entrada en el blog, así que espero os guste y os sea de utilidad. En la medida del tiempo disponible ire añadiendo nuevas entradas.











Código:


#include "cmbtll12.ch"

FUNCTION Disenya()

   LOCAL oLL,oWnd,hJob,cName:='qsl'

   oLl = TActiveX():New( oWnd, "L12.List-Label12_Ctrl_32.1" )

   oLl:Do( "LlSetOption", LL_OPTION_MULTIPLETABLELINES, 1)    //Permite multiples tablas

   oLl:Do( "LlDefineFieldStart" ) //Limpia buffer de campos

   oLl:DO( "LlDefineVariableStart")//Limpia buffer de variablesx

   Tmp->( DefineData(.t., oLl) )



   oLl:Do("LlSetPrinterDefaultsDir",hJob,"C:\Windows\Temp") //Arranca diseñador

   oLl:Do("LlDefineLayout",oWnd:hWnd,"Designer",LL_PROJECT_LIST, "log.lst")



RETURN NIL

//------------------------------------

FUNCTION Imprime()

   LOCAL oLL,oWnd,hJob,cName:='log.lst',cDir:=oAppl:cDirLocal

   oLl = TActiveX():New( oAppl:oWnd, "L12.List-Label12_Ctrl_32.1" )

   oLl:Do( "LlSetOption", LL_OPTION_MULTIPLETABLELINES, 1)

   oLl:Do( "LlDefineFieldStart" )

   oLl:DO( "LlDefineVariableStart")

   Tmp->( DefineData(.t., oLl) )

   oLl:Do("LlSetPrinterDefaultsDir",cDir)

   oLl:Do("LlPrintStart",LL_PROJECT_LIST,cName,LL_PRINT_PREVIEW)

   oll:DO("llPrintOptionsDialog",oAppl:oWnd:hWnd)

   oLl:DO("llPrint")

   Tmp->(DBGoTop())

   DO WHILE !Tmp->(Eof())

      Tmp->(DefineData(.T.,oLl))

      oLl:DO("llPrintFields")

      Tmp->(DBSkip())

   ENDDO

   oll:DO("llPrintEnd",0)

   oLl:DO("LlPreviewDisplay", cName, , oAppl:oWnd:hWnd)

   oLl:DO("llPreviewDeleteFiles",cName,cDir)

RETURN NIL

//----------------------------------------------

FUNCTION DefineData(lAsField,oLl)

   LOCAL FldType, FldContent, lExpr,i,DateBuffer:=Replicate(Chr(0),255)

   //Convierte formato xBase a List & Label

   FOR i:=1 TO FCount()

      DO CASE

      CASE FieldType(i)=='N'

         FldType:=LL_NUMERIC

         FldContent:=Str(FieldGet(i))

      CASE FieldType(i)=='C'

         FldType:=LL_TEXT

         FldContent:=Trim(FieldGet(i))

      CASE fieldType(i)=='M'

         FldType:=LL_TEXT

         FldContent:=FieldGet(i)

      CASE FieldType(i)=='L'

         FldType:=LL_BOOLEAN

         FldContent:=IF(FieldGet(i),'TRUE','FALSE')

      CASE FieldType(i)=='D'

         FldType:=LL_TEXT

         FldContent:=DToC(FieldGet(i))

      ENDCASE

      IF lAsField

         oLl:Do("LlDefineFieldExt",FieldName(i), FldContent, FldType )

      ELSE

         oLl:Do("LlDefineVariableExt", FieldName(i), FldContent, FldType )

      ENDIF

   NEXT

RETURN NIL