FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Problema usando tDatabase
Posts: 682
Joined: Tue Feb 14, 2006 09:48 AM
Problema usando tDatabase
Posted: Wed Nov 28, 2007 04: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.
#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/
Posts: 3358
Joined: Fri Oct 07, 2005 08:20 PM
Problema usando tDatabase
Posted: Wed Nov 28, 2007 04: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
Posts: 682
Joined: Tue Feb 14, 2006 09:48 AM
Problema usando tDatabase
Posted: Wed Nov 28, 2007 05: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/
Posts: 8515
Joined: Tue Dec 20, 2005 07:36 PM
Problema usando tDatabase
Posted: Wed Nov 28, 2007 05: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鈥∶唎 (Cancelar) Funciona.
ELSE
MsgStop( "Banco de Dados Bloqueado", "Cuidado!" )
RETURN NIL
ENDIF

IF lAppend  //-&gt; Inclusao

    DbClientes:SetOrder( 1 )
    DbClientes:GoBottom()

    nCodigo := ( DbClientes:Raz_Matric )

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

 ELSE //-&gt; Altera鈥∶唎

    nRecNo := (DbClientes:cAlias)-&gt;( RecNo() )
    DbClientes:GoTo( nRecNo )
    nOldRecNo := (DbClientes:cAlias)-&gt;( RecNo() )

ENDIF

TRAVEREG(0)  //-&gt; APPEND BLANK O DBAPPEND() Y TRABA DEL REGISTRO
DbClientes:Save()
DESTRAVA(0)

*/

Jo茫o Santos - S茫o Paulo - Brasil - Phone: +55(11)95150-7341
Posts: 682
Joined: Tue Feb 14, 2006 09:48 AM
Problema usando tDatabase
Posted: Wed Nov 28, 2007 05: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.

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.
//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.
IF lAdd //-> Para Anadir 
oDbf:SetBuffer( .T. ) 
oDbf:Blank() 
ELSE //-> Para Alterar 
oDbf:Load() 
ENDIF

Este codigo es practicamente igual al que yo uso
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/
Posts: 8515
Joined: Tue Dec 20, 2005 07:36 PM
Problema usando tDatabase
Posted: Wed Nov 28, 2007 05: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
Posts: 205
Joined: Fri Oct 07, 2005 05:07 PM
Problema usando tDatabase
Posted: Wed Nov 28, 2007 06:55 PM
Experimenta esto
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
Saludos/regards

RenOmaS



skype: americo.balboa
Posts: 1445
Joined: Mon Oct 10, 2005 02:38 PM
Re: Problema usando tDatabase
Posted: Wed Nov 28, 2007 07: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.

Un Saludo

Carlos G.



FiveWin 25.12 + Harbour 3.2.0dev (r2502110321), BCC 7.7 Windows 11 Home

Posts: 1445
Joined: Mon Oct 10, 2005 02:38 PM
Re: Problema usando tDatabase
Posted: Wed Nov 28, 2007 07: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.

Un Saludo

Carlos G.



FiveWin 25.12 + Harbour 3.2.0dev (r2502110321), BCC 7.7 Windows 11 Home

Posts: 711
Joined: Thu Oct 06, 2005 09:57 PM
Problema usando tDatabase
Posted: Thu Nov 29, 2007 05: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
Posts: 1283
Joined: Fri Feb 10, 2006 02:34 PM
Problema usando tDatabase
Posted: Thu Nov 29, 2007 09: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:

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 :-) . 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
HIX -> https://github.com/carles9000/hix
Posts: 682
Joined: Tue Feb 14, 2006 09:48 AM
Problema usando tDatabase
Posted: 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:
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/
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Problema usando tDatabase
Posted: Thu Nov 29, 2007 01: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
Posts: 189
Joined: Wed Apr 05, 2006 09:48 PM
Problema usando tDatabase
Posted: Thu Nov 29, 2007 01: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

Posts: 1088
Joined: Fri Oct 07, 2005 03:33 PM
Problema usando tDatabase
Posted: Thu Nov 29, 2007 03: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:&lt;campo&gt;, 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