Problema usando tDatabase

Problema usando tDatabase

Postby Biel EA6DD » Wed Nov 28, 2007 4:19 pm

Hola foro,
Necesito ayuda, estoy algo confuso, y no doy con la solución del siguiente problema.

Quiero utilizar la clase tDatabase, y la estoy probando con un sencillo mantenimiento de fichero maestro. Visualizo una tabla en un browse, y edito o añado datos en un dialogo.
Pretendo que solo exista un objeto tDatabase, que es el que uso en el browse y en dialogo.

He aqui el problema, puesto que al al intentar añadir registros, ejecuto el metodo Blank, que deja vacio el buffer de edicion, pero mientras estoy editando, o al cerrar el dialogo, supongo que desde el browse se ejecuta el metodo load, con lo que el buffer que estoy editando se carga con los datos del registro sobre el que esta posicionado el browse.
No se si me explico, aqui os dejo un ejemplo autocontenido, probad de dar de alta un registro.
Code: Select all  Expand view
#include "Fivewin.ch"
#include "xBrowse.ch"
STATIC oDbf
FUNCTION MAIN()
   LOCAL oWnd,oBrw,oCol,oBar
   CrtTmp()
   oDbf:=tDatabase():New()
   DEFINE WINDOW oWnd
   DEFINE BUTTONBAR oBar OF oWnd
   *-
   DEFINE BUTTON OF oBar ACTION EdtTmp(.t.) PROMPT 'Altas'
   DEFINE BUTTON OF oBar ACTION EdtTmp(.f.) PROMPT 'Modif'
   *--
   oBrw := TXBrowse():New(oWnd)
   oBrw:nMarqueeStyle       := MARQSTYLE_HIGHLROW
   oBrw:nColDividerStyle    := LINESTYLE_BLACK
   oBrw:lColDividerComplete := .T.
   *--
   oCol:=oBrw:AddCol()
   oCol:bStrData:= {||oDbf:Cod}
   oCol:cHeader := 'Cod.'
   *--   
   oCol:=oBrw:AddCol()
   oCol:bStrData:= {||oDbf:Nom}
   oCol:cHeader := 'Descripción'
   *--
   oBrw:bGoTop    := {|| oDbf:GoTop() }
   oBrw:bGoBottom := {|| oDbf:GoBottom() }
   oBrw:bSkip     := {| n | iif( n == nil, n := 1, ), oDbf:Skipper( n )  }
   oBrw:bBof      := {|| oDbf:Bof()  }
   oBrw:bEof      := {|| oDbf:Eof()}
   oBrw:bBookMark := {| n | iif( n == NIL, oDbf:RecNo() , oDbf:Goto( n ) )  }
   
   oBrw:CreateFromCode()

   oWnd:oClient := oBrw
   oBrw:lRecordSelector:=.t.
   
   ACTIVATE WINDOW oWnd
   RETURN NIL
//-------------------------------
STATIC FUNCTION EdtTmp(lAdd)
   LOCAL oDlg,lSave := .F.,oFont,oBtn, cCod,cNom
   //LOCAL oDbf:=tDatabase():New()  <<<<<<<<<<<<<<<<Si defino otro objeto local, funciona Ok.
   IF (lAdd,oDbf:Blank(),oDbf:Load())
   
   DEFINE FONT oFont NAME "MS Sans Serif" SIZE 0, 8
   DEFINE DIALOG oDlg FROM 100, 100 TO 230,494 PIXEL FONT oFont
   oDlg:SetText('('+ProcName()+') '+ IF(lAdd,'Alta','Modificación')+' de registros' )
   @ 12, 10 SAY "Cod:" OF oDlg SIZE 15, 8 PIXEL FONT oFont
   @ 26, 10 SAY "Nom:" OF oDlg SIZE 17, 8 PIXEL FONT oFont
   @ 10, 32 GET oDbf:Cod OF oDlg SIZE 15, 12 PIXEL FONT oFont WHEN lAdd
   @ 24, 32 GET oDbf:Nom OF oDlg SIZE 155, 12 PIXEL FONT oFont

   @ 46, 107 BUTTON oBtn PROMPT "OK" OF oDlg SIZE 42, 14 PIXEL FONT oFont DEFAULT ;
                         ACTION (MsgStop("Valor nombre antes de cerrar el dialogo:"+CRLF+oDbf:Nom),oDlg:End(), lSave := .T.)
   @ 46, 151 BUTTON oBtn PROMPT "Cancelar" OF oDlg SIZE 42, 14 PIXEL FONT oFont CANCEL ACTION (oDlg:End())

   ACTIVATE DIALOG oDlg CENTERED
   MsgStop("Valor nombre despues de cerrar el dialogo:"+CRLF+oDbf:Nom)
   IF lSave
      IF lAdd
         oDbf:Append()
      ENDIF
      oDbf:Save()
   ENDIF
RETURN NIL
//-------------------------------
STATIC FUNCTION CrtTmp()
   LOCAL i
   DBCreate('tmp.dbf',{;
                        { "COD"       , "C",     2,    0 },;
                        { "NOM"       , "C",    30,    0 } ;                                       
                      })   
   DBUseArea(.T.,,'tmp',,.T.)
   
   FOR i:=1 TO 50
       Tmp->(dbAppend())
       Tmp->Cod:=Str(i,2)
       Tmp->Nom:='Nombre '+Tmp->Cod
   NEXT         
   Tmp->(dbGoTop())
   RETURN NIL

Veis alguna posible solucion, o por el contrario la unica via es crear dos objetos tDatabase, para que sea independiente el browse del dialogo.
Ahora que escribo esto, estoy pensando que si ejecuto el metodo blank en el browse y no el dialogo, puede que tambien funcione, ya que tanto el browse como el dialogo estarian sobre un registro vacio.
Saludos desde Mallorca
Biel Maimó
http://bielsys.blogspot.com/
User avatar
Biel EA6DD
 
Posts: 682
Joined: Tue Feb 14, 2006 9:48 am
Location: Mallorca

Postby Armando » Wed Nov 28, 2007 4:53 pm

Biel:

Prueba utilizando el alias de la DBF en el browse y el objeto oDbf para edición, ejemplo del browse:

*--
oCol:=oBrw:AddCol()
oCol:bStrData:= {||cAlias->Cod}
oCol:cHeader := 'Cod.'
*--

De esta forma no he tenido problemas.

Saludos
SOI, s.a. de c.v.
estbucarm@gmail.com
http://www.soisa.mex.tl/
http://sqlcmd.blogspot.com/
Tel. (722) 174 44 45
Carpe diem quam minimum credula postero
User avatar
Armando
 
Posts: 3070
Joined: Fri Oct 07, 2005 8:20 pm
Location: Toluca, México

Postby Biel EA6DD » Wed Nov 28, 2007 5:03 pm

Gracias Armando, por la respuesta.
Bueno la idea original es no usar alias, para que en un futuro y sin retocar más de una linea (oDbf:= ...) pueda servirme indistintamente para DBF como para acceso via ADO.
Saludos desde Mallorca
Biel Maimó
http://bielsys.blogspot.com/
User avatar
Biel EA6DD
 
Posts: 682
Joined: Tue Feb 14, 2006 9:48 am
Location: Mallorca

Postby karinha » Wed Nov 28, 2007 5:04 pm

//IF (lAdd,oDbf:Blank(),oDbf:Load())

SELECT( oDbf:cAlias )
DATABASE oDbf

IF lAdd //-> Para Anadir
oDbf:SetBuffer( .T. )
oDbf:Blank()
ELSE //-> Para Alterar
oDbf:Load()
ENDIF

O

/*
IF NetUse( "CADETIQ", .T. )
OrdListAdd( "CADETIQ", "RAZ_MATRIC", "DEST_NOME", "END", ;
"NOME_ARTIS", "CPF", "CNPJ", ;
"TELEFONE" )
OrdDescend( ,,.F. ) // - Decrescente
DATABASE DbClientes // Como Escrevemos em Ingles
DbClientes:Gotop()
DbClientes:Load()
DbClientes:SetBuffer( .T. ) // Assim, .T. a Op‡Æo (Cancelar) Funciona.
ELSE
MsgStop( "Banco de Dados Bloqueado", "Cuidado!" )
RETURN NIL
ENDIF

IF lAppend //-> Inclusao

DbClientes:SetOrder( 1 )
DbClientes:GoBottom()

nCodigo := ( DbClientes:Raz_Matric )

DbClientes:SetBuffer( .T. )
DbClientes:Blank()

ELSE //-> Altera‡Æo

nRecNo := (DbClientes:cAlias)->( RecNo() )
DbClientes:GoTo( nRecNo )
nOldRecNo := (DbClientes:cAlias)->( RecNo() )

ENDIF

TRAVEREG(0) //-> APPEND BLANK O DBAPPEND() Y TRABA DEL REGISTRO
DbClientes:Save()
DESTRAVA(0)
*/
João Santos - São Paulo - Brasil - Phone: +55(11)95150-7341
User avatar
karinha
 
Posts: 7303
Joined: Tue Dec 20, 2005 7:36 pm
Location: São Paulo - Brasil

Postby Biel EA6DD » Wed Nov 28, 2007 5:34 pm

Gracias João,
He probado la primera parte de tu codigo, y me produce el mismo efecto erroneo. Das de alta un registro, pero graba los datos del registro sobre el que esta posicionado el browse.

Code: Select all  Expand view
SELECT( oDbf:cAlias )
DATABASE oDbf

oDbf esta definida como variable estatica, asi lo unico que hace es volver a crear un objeto tDatabase en la misma variable.
Si oDbf la definimos local, funciona bien, pero me gustaria no terner que definrla local a la funcion. La idea es usar un solo oDbf generico para todo el programa.
Code: Select all  Expand view
//LOCAL oDbf:=tDatabase():New()  <<<<<<<<<<<<<<<<Si defino otro objeto local, funciona Ok.
Si descomentas esta linea del codigo que he puesto, veras como funciona ok, sin cambiar nada mas.
Code: Select all  Expand view
IF lAdd //-> Para Anadir
oDbf:SetBuffer( .T. )
oDbf:Blank()
ELSE //-> Para Alterar
oDbf:Load()
ENDIF

Este codigo es practicamente igual al que yo uso
Code: Select all  Expand view
IF (lAdd,oDbf:Blank(),oDbf:Load())

varia en que fuerzas el uso del buffer cn Setbuffer(.t.), pero por defecto lBuffer esta a verdadero cuando se crea el objeto, SetBuffer ademas de poner lBuffer a .T. fuerza un Load, pero no varia el comportamiento.
Saludos desde Mallorca
Biel Maimó
http://bielsys.blogspot.com/
User avatar
Biel EA6DD
 
Posts: 682
Joined: Tue Feb 14, 2006 9:48 am
Location: Mallorca

Postby karinha » Wed Nov 28, 2007 5:42 pm

nRecNo := (DbClientes:cAlias)->( RecNo() )
DbClientes:GoTo( nRecNo )

nOldRecNo := (DbClientes:cAlias)->( RecNo() )

Cuando en la Gravacion:

GoTo( nOldRecno )
João Santos - São Paulo - Brasil - Phone: +55(11)95150-7341
User avatar
karinha
 
Posts: 7303
Joined: Tue Dec 20, 2005 7:36 pm
Location: São Paulo - Brasil

Postby RenOmaS » Wed Nov 28, 2007 6:55 pm

Experimenta esto
Code: Select all  Expand view
DEFINE BUTTON OF oBar ACTION EdtTmp(.t., oClone( oDbf )) PROMPT 'Altas'
DEFINE BUTTON OF oBar ACTION EdtTmp(.f., oClone( oDbf )) PROMPT 'Modif'
...
...

STATIC FUNCTION EdtTmp(lAdd, oDbf )
   LOCAL oDlg,lSave := .F.,oFont,oBtn, cCod,cNom
   IF (lAdd,oDbf:Blank(),oDbf:Load())

.....




Salu2
User avatar
RenOmaS
 
Posts: 205
Joined: Fri Oct 07, 2005 5:07 pm

Re: Problema usando tDatabase

Postby FiveWiDi » Wed Nov 28, 2007 7:31 pm

Biel,

prueba con esto:

*--
oCol:=oBrw:AddCol()
oCol:bStrData:= {|| (oDbf:cAlias)->Cod }
oCol:cHeader := 'Cod.'
*--

De esta manera el browse estará leyendo directamente del campo de la DBF, prescindiendo del buffer.

Saludos
Carlos G.
FiveWiDi
 
Posts: 1068
Joined: Mon Oct 10, 2005 2:38 pm

Re: Problema usando tDatabase

Postby FiveWiDi » Wed Nov 28, 2007 7:40 pm

Biel,

olvida mi primer mensaje, entendí mal.

Ahora bien si haces:

ACTION ( MsgStop("Valor nombre antes de cerrar el dialogo:"+CRLF+oDbf:Nom), ;
If(lAdd, oDbf:Append(),Nil), ;
oDbf:Save(), ;
oDlg:End() )


Creo que conseguirás lo que quieres.

Saludos
Carlos G.
FiveWiDi
 
Posts: 1068
Joined: Mon Oct 10, 2005 2:38 pm

Postby Manuel Valdenebro » Thu Nov 29, 2007 5:46 am

Gabriel,

Desde hace tiempo vengo utilizando la clase Database con Browse, sin ningun tipo de problemas. No comprendo para que necesitas crear el objeto oDbf en el Browse. En el Browse, yo me muevo directamente con los registros de la tabla (DBGOTOP(), DBGOBOTTOM, etc.) y es en la funcion de ALTA y MODIFICACION, donde genero el objeto oDbf como cvariable Local y en su caso, doy de alta un nuevo registro o lo modifico el registro. Al volver al browse, el registro ya esta dado de alta o modificado en la dbf y por tanto realizo un refresh(). Tal cual tu lo haces, creo no va a funcionar, sobretodo, en las altas.

Te envio parte de un código a tu correo, por si fuera de tu interes.

Si deseas continuar, de todos modos, con el mismo sistema, en mi opinión, podrias hacer lo siguiente:

1) Dar de alta en la funcion Browse y como Local oDbf.
2) Pasar oDbf como parametro a la funcion Alta/Modificar y que esta funcion devolviera en Return (oDbf).

P.D.: Te he enviado un correo a tu dirección xxxx@ctv.es pero ha venido devuelto.
Un saludo

Manuel
User avatar
Manuel Valdenebro
 
Posts: 706
Joined: Thu Oct 06, 2005 9:57 pm
Location: Málaga-España

Postby Carles » Thu Nov 29, 2007 9:04 am

Hola Biel,

Hace tiempo me encontre con este caso, y el problema esta en que al crear un dialogo, constantemente puedes mandar mensajes de ::Display() a la ventana del TXBrowse, con lo q evalua el metodo ::Paint() y este constantemente esta realizando movimientos de ::Skip(), con lo q es normal esta falta de control del puntero del registro.

Puedes intentar varias soluciones (en principio poco elegantes) para engañar al sistema, pero lo mejor es (por si quieres seguir usando objetos TDatabase y que la aplicacion quede igual tanto para DBF como ADo) el crear, cuando entras en una funcion de mantenimiento, otro objeto q se cree a partir del oDbf del Browse. Al salir del mantenimiento refrescaras el oDbf del Browse y listo. No es decabellado y tienes el completo control tanto en el Browse como en el dialogo.

Yo recuerdo que intente cuando entraba en la funcion de mantenimento:

Code: Select all  Expand view


FUNCTION Manteniment()
...
oBrw:lCreated := .F. 
...

Dialogo


oBrw:lCreated := .T. // Al final de la funcion

RETU NIL



Con esto evitaba q se evaluara el metodo ::Paint() con el consiguiente movimiento del puntero del oDbf, pero me quedaba pendiente la solucion del refresco de la pantalla por si movia un dialogo por encima. Total, q empeze a intentar redefinir el metodo ::Paint(), pero desisti :x . Creee un metodo para crear objetos TDatabase a partir de otro y listos.

Salutacions.
C.
Salutacions, saludos, regards

"...programar es fácil, hacer programas es difícil..."

UT Page -> https://carles9000.github.io/
Forum UT -> https://discord.gg/bq8a9yGMWh
Skype -> https://join.skype.com/cnzQg3Kr1dnk
User avatar
Carles
 
Posts: 1096
Joined: Fri Feb 10, 2006 2:34 pm
Location: Barcelona

Postby Biel EA6DD » Thu Nov 29, 2007 10:20 am

Muchas gracias a todos, la verdad no esperaba tantas respuestas. Me encanta el foro, y que siempre haya gente dispuesta a ayudar.

Voy a ir por partes, lo primero de todo es explicar porque quiero usar tDatabase en el browse y en el dialogo.

La verdad es que es complicarse un poco la vida, de hecho yo hasta ahora no lo usaba así, sino que el browse lo referencia con el alias de la tabla, y en el dialogo de mantenimiento creaba el objeto tDatabase.
Pero claro, soy un poco masoca y me gusta experimentar y darle otra vuelta de tuerca a mis programas, entonces pensando en la posibilidad de usar difierentes motores de base de datos, pense que una buena manera de aislarlo era usar la clase tDatabse por todos lados, y no usar el alias de la tabla. El segundo paso seria crearme una clase clon a tDatabase con los mismos metodos, pero atacando origenes de dato SQL via ADO.
De esta forma cambiando la linea donde se crea oDbf, un mismo programa podria ser contra DBF, o cualquier origen ADO.
Por ejemplo:
Code: Select all  Expand view
IF lAdo
     oDbf:=tAdoDb():New() //Clase para manejor de Rs ADO
ELSE
     oDbf:=tDatabase():New()
ENDIF


Tras leer los mensajes, he revisado las soluciones que me comentais, todas son funcionales, pero por el motivo que os he explicado antes, no son exactamente el tipo de solución que estoy buscando.
El controlar el puntero como comenta Joao, es complicado, mas aun cuando el metodo blank se mueve al registro posterior a Eof, y luego reposiciona en registro actual.

Cambiar el lugar donde se crea y graba el registro tampoco es valido, pues con solo mover el dialogo, podreis ver que el buffer de edicion cambia.

Lo mas sencillo de aplicar, sin usar alias, parece que pasa por clonar el objeto tDatabase, o crear uno de nuevo en el dialogo, en principio esa idea tampoco me gustaba demasiado, pero por ahora me parece la menos mala.

Sin duda como comenta Carles, el problema es que la ventana que tiene el browse va recibiendo y evaluando mensajes que hacen que el buffer de edicion cambie. Intentare investigar algo mas por las vias que comenta Carles, y sino pues clonaremos objetos.
Saludos desde Mallorca
Biel Maimó
http://bielsys.blogspot.com/
User avatar
Biel EA6DD
 
Posts: 682
Joined: Tue Feb 14, 2006 9:48 am
Location: Mallorca

Postby Antonio Linares » Thu Nov 29, 2007 1:30 pm

Biel,

Asi es, como comenta Carles, el browse recibe mensajes de pintado y eso afecta al objeto TDataBase que usas tambien desde el diálogo

Tal vez te sirva abrir la misma DBF en otra area de trabajo simultaneamente, para que asi una no afecte a la otra
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 41351
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Postby Frafive » Thu Nov 29, 2007 1:33 pm

Hola Biel

Yo lo que hago es clonar el objeto cuando entro en modo de edicion y despues tambien he modificado el metodo blank y me funciona perfectamente.

un saludo


METHOD Blank() CLASS TDataBase

local nFor, nLen
local cType

::aBuffer=Array(::FCount() )

nLen :=len(::aBuffer)

for nFor := 1 to nLen
cType := ::FieldType( nFor )



do case
case cType == "C"
::aBuffer[ nFor ] := Space(::FieldLen( nFor ) )
case cType == "D"
::aBuffer[ nFor ] := Ctod("")
case cType == "N"
::aBuffer[ nFor ] := 0
case cType == "L"
::aBuffer[ nFor ] := .f.
case cType == "M"
::aBuffer[ nFor ] := ""
end case

next

Return Self
Frafive
 
Posts: 189
Joined: Wed Apr 05, 2006 9:48 pm

Postby Marcelo Via Giglio » Thu Nov 29, 2007 3:13 pm

Biel,

yo hice una clase Tarray para trabajar con arrays como si fuera un dbf (hay varias implemenatciones de esto po ahi) y claro esta, hay cosas basadas en tDatabase, pero este problema que mencionas tambien se reproducia, como lo solucione?, es crear un buffer adicional, algo si

oData := dbData:buffer_blank() // te devuelve una estructura en blanco similar a la DB y pudes utilizar oData:<campo>, etc
oData := dbData:buffer_load() // te devuelve la estructura con los datos del reg actual
dbData:buffer_save( oData ) // salva el buffer en el registro actual

Espero esto te ayude

saludos

Marcelo
Marcelo Via Giglio
 
Posts: 1050
Joined: Fri Oct 07, 2005 3:33 pm
Location: Cochabamba - Bolivia

Next

Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: No registered users and 7 guests