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)

15 comentarios:

Carlos Mora dijo...

Ya estaba extrañando tus artículos, como siempre muy buenos.

A ver si nos ponemos de acuerdo en algun momento para coordinar la reunión que ya hablamos alguna vez.

Biel Maimó dijo...

Gracias Carlos.

Pues tendriamos que reactivar el tema, los meses van pasando y no hemos hecho nada.

Un dia de estos hablamos, saludos.

Anónimo dijo...

Amigo biel como estas?

De verdad que interesante este articulo de verdad que si.. Me han servido mucho tu blog de nuevo gracias.

Leandro Alfonso
Bogotá (Colombia)

Anónimo dijo...

Amigo que pena molestarte de nuevo con mis preguntas...

Pero como hago para ubicarme en un registro de un xbrowse; por ejemplo quiero que al momento de hacer una consulta si estoy en el registro 15 de 40; cuando refresque de nuevo el recorset y el xbrowse, este se posicione de nuevo en el registro 15.

De antemano gracias

Saludos

LEANDRO ALFONSO
Bogotá (Colombia)

Biel Maimó dijo...

Hola Leandro,
si a lo que te reieres es a ejecutar una nueva consulta contra el servidor, y como resultado tengas un nuevo set de regitros, entonces para posicionarte sobre uno en concreto puedes hacerlo con el metodo Skip del xBrowse.

Biel Maimó dijo...

Hola Leandro,
si a lo que te reieres es a ejecutar una nueva consulta contra el servidor, y como resultado tengas un nuevo set de regitros, entonces para posicionarte sobre uno en concreto puedes hacerlo con el metodo Skip del xBrowse.

Anónimo dijo...

Ok amigo muchas gracias voy a probar y si algo te molestare de nuevo con mis preguntas....

Por cierto muy bueno el articulo sobre leto.... tambien de mucha utilidad

Anónimo dijo...

Me ha sido de mucha utilidad el artículo, y me funcionó perfecto, excepto porque al momento de que encuentra se borran los headers dle xbrowse quedando horrible. Para que vuelvan aparecer tengo que darle a inicio o al fin. Alguna forma de arreglarlo?

Marcelo Jingo
(Ecuador)

Biel Maimó dijo...

Supongo que no estas con la ultima version de FiveWin, esto que comentas es un problema de la clase xBrowse, para solucionarlo, busca la cadena
lOnlyData := ::lRefreshOnlyData
y sustiuyela por
lOnLyData := .f.

Armando dijo...

Biel:

Estoy probando ese ejemplo y me surge una duda.

Por qué usar un SAY en lugar de un GET ?

Saludos y muy buen artículo.

Armando

Biel Maimó dijo...

Hola Armando, xBrowse provve de un metodo de busqueda incremental, y para su implementacion no usa Get, va capturando las teclas pulsadas.
Entonces la busqueda funciona sin SAY y sin GET, lo del SAY es para que veamos lo tecleado. Supongo que tambien se podria implementar con un GET, pero quizas deberas de retocar la clase xBrowse.
Saludos.

Mario Rodriguez dijo...

Hola, me parece muy interesante tu articulo, pero mi problema es que yo estoy usando un browse muy basico y quisiera obtener el mismo resultado, hacer busqueda incremental e indezación de mi browse al dar click en el nombre de la columna, te paso parte de mi código.

define dialog oDialsv resource "DLGVERDB6" of oVentprinc title "Browse No. 1"
redefine listbox oLbx1sv FIELDS clientes->nombre,clientes->direccion,cliente->estado ID 1 of oDialsv alias "clientes";
headers "Nombre","Direccion","Estado"
redefine button oBtn2sv ID 24 of oDialsv action (oDialsv:end(),cicsur:="N")
activate dialog oDialsv center


Espero puedas ayudarme. Muchas gracias por anticipado

Juan dijo...

Amigo BielSys, estoy empezando con xbrowse antes usaba el listbox, segui tu ejemplo y me funciona bien pero cuando trato de cambiar el nombre a las columnas y trato de darle la anchura correspondiente me da error de indice(bound error array acces), justo cuando empiezo a definir las columas .. este es el codigo con esas instruciones agregadas.. uso fhw 6.12 y su equivalente xharbour

/*
Ver tabla de una base de datos acces con xBrose,
implementa metod de ordenación y busqueda incremental
(c) 2008 Biel Maimo bmaimo@gmail.com - bielsys.blogspot.com
*/

#include "FiveWin.ch"
#Include "Ado.ch"
#include "xBrowse.ch"

FUNCTION VerMYSQL(oWindow)
LOCAL oWndVerMdb,oRs, oBrw, oCol
LOCAL oBar, oSay



DEFINE WINDOW oWndVerMdb MDICHILD OF oWindow FROM 5,0 TO 20,50 TITLE "Visualizar tabla CUENTAS" ;

oBar := TBar():New(oWndVerMdb,50,45,.T.)

@ 01,10 SAY oSay PROMPT '' SIZE 90,20 OF oBar

//--
* oCnn:=tOleAuto():new("ADODB.connection")
* oCnn:open("Provider= MicroSoft.Jet.OLEDB.4.0;Data Source=.\demo.mdb;")


// -- Creamos RecordSet----
TRY
oRs:=TOleAuto():New("adodb.recordset")
CATCH oError
MsgStop( "No se ha podido crear el RECORDSET de Cuentas Contables !", oApp:cAplicacion)
ShowError(oError)
oRs:= NIL
RETURN(.F.)
END
oRs:CursorLocation := adUseClient
oRs:CursorType := adOpenDynamic
oRs:LockType := adLockOptimistic
oRs:Source := "SELECT * FROM WCUENTAS ORDER BY CUENTA"
oRs:ActiveConnection(oApp:oCon)
TRY
oRs:Open()
CATCH oError
MsgStop( "No se ha podido abrir el RECORDSET de Cuentas Contables !", oApp:cAplicacion)
ShowError(oError)
RETURN(.F.)
END
//--

oBrw := TXBrowse():New( oWndVerMdb )


* le voy agregando esto

oBrw:nMarqueeStyle := MARQSTYLE_HIGHLROW // Con cursor de todo el renglón
oBrw:nColDividerStyle := LINESTYLE_LIGHTGRAY
oBrw:nRowDividerStyle := LINESTYLE_LIGHTGRAY
oBrw:lColDividerComplete := (.F.) // (.T.) Mantiene siempre llena la pantalla
oBrw:nHeaderHeight := 25 // Altura de los encabezados
oBrw:nRowHeight := 22 // Altura del renglón
oBrw:nHeaderLines := 1 // Número de líneas de encabezados
oBrw:nDataLines := 1 // Número de líneas de detalle
oBrw:nFooterHeight := 20 // Altura del footer
oBrw:nFooterLines := 1 // Número de líneas en el footer
oBrw:lFooter := (.T.) // Sí queremos línea de footer
oBrw:lHScroll := (.F.) // Quitamos el scroll horizontal

oBrw:aCols[1]:cHeader := "Cuenta"
oBrw:aCols[1]:nHeadStrAlign := AL_LEFT
oBrw:aCols[1]:nDataStrAlign := AL_LEFT
oBrw:aCols[1]:nWidth := 15

oBrw:aCols[2]:cHeader := "Descripcion de la Cuenta"
oBrw:aCols[2]:nHeadStrAlign := AL_LEFT
oBrw:aCols[2]:nDataStrAlign := AL_LEFT
oBrw:aCols[2]:nWidth := 210

oBrw:aCols[3]:cHeader := "Grupo"
oBrw:aCols[3]:nHeadStrAlign := AL_LEFT
oBrw:aCols[3]:nDataStrAlign := AL_LEFT
oBrw:aCols[3]:nWidth := 10

oBrw:aCols[4]:cHeader := "Tipo"
oBrw:aCols[4]:nHeadStrAlign := AL_LEFT
oBrw:aCols[4]:nDataStrAlign := AL_LEFT
oBrw:aCols[4]:nWidth := 10

oBrw:aCols[5]:cHeader := "Sdoi"
oBrw:aCols[5]:nHeadStrAlign := AL_LEFT
oBrw:aCols[5]:nDataStrAlign := AL_RIGTH
oBrw:aCols[5]:nWidth := 50




oBrw:bSeek := { | c | Busca(c,oRs) }
oBrw:oSeek := oSay
oBrw:SetAdo(oRs)
AEval(oBrw:aCols,{|oCol|oCol:bLClickHeader:={|f,c,h,oCol|Ordena(oBrw,oRs,oCol)}})
oBrw:CreateFromCode()
oWndVerMdb:oClient:=oBrw

ACTIVATE WINDOW oWndVerMdb ON INIT oBrw:SetFocus()
RETURN NIL

Juan dijo...

Problema solucionado, tenia que poner antes de identificar las columnas el setado, cosa logica, perdonen mi ignorancia.. gracias..

Anónimo dijo...

Amigo excelente lo de la búsqueda incremental....te felicito me ha facilitado mucho las búsquedas dentro de un recorset ya creADO...

De nuevo mil gracias.

LEANDRO AREVALO