Recientement me veo en la necesidad de construir un control que muestre 10 botones en un dialogo:
*4 de ellos son para navegar por la tabla ( Inicio, anterior, siguiente, final), y otros botones para *Localizar, *Listar, *Agregar, *Editar, *Borrar y *Cerrar.
y que en modo Edicion o Agregar registro (*Agregar y Editar) cambien su funcion a *Grabar y *Deshacer, y luego de grabar o descartar los cambios retomen su funcion inicial.
Mi necesidad es que deseo abandonar la modalidad browse, para centrame en el registro a registro.
por motivo que la app sera cliente/servidor, y todos sabemos que los browse no comulgan bien con esta arquitectura.
he mirado un control en VFP que me gusta y es a lo que aspiro a realizar.
En este momento he avanzado algo, esto es lo que tengo:
una clase para el manejo del movimiento del puntero en la tabla ( TBtnNav ), y una clase derivada para las otras acciones a realizar. ( TBtnABC )
http://www.megaupload.com/?d=J0QMNN6H
test.prg
- Code: Select all Expand view RUN
PROCEDURE Test()
LOCAL oDlg
LOCAL oBNav
USE D:\FWH\SAMPLES\CLIENTES.DBF NEW ALIAS CUST
DEFINE DIALOG oDlg NAME "TEST"
//oBNav := TBtnNav():new( oDlg, "CUST" ) //primera clase
oBNav := TBtnABC():new( oDlg, "CUST" ) //segunda clase
oBNav:bOnChange := { | o | msginfo( (o:cAlias)->NOMBRE ) }
oBNav:SetAction( 10, {|| oDlg:END() }, "CERRAR TEST" )
ACTIVATE DIALOG oDlg CENTER
USE
RETURN
clases.prg
- Code: Select all Expand view RUN
/*-------------------------------------------------------------------------------------------------*/
/*incluye fichero cabezera de sistema*/
#include "hbclass.ch"
#include "fivewin.ch"
/*-------------------------------------------------------------------------------------------------*/
#define BN_TOP 01
#define BN_BACK 02
#define BN_NEXT 03
#define BN_END 04
#define BN_LOCATE 05
#define BN_PRINT 06
#define BN_NEW 07
#define BN_EDIT 08
#define BN_DELETE 09
#define BN_CLOSE 10
/*-------------------------------------------------------------------------------------------------*/
CLASS TBtnNav
DATA oDlg
DATA cAlias
DATA aoButtons
DATA abActions
DATA bOnChange
METHOD New( oDlg, cAlias, bOnChange )
METHOD DisableAll() INLINE AEval( ::aoButtons, { |o| o:disable() } )
METHOD EnableAll() INLINE AEval( ::aoButtons, { |o| o:enable() } )
METHOD SetAlias( cAlias )
METHOD Navigate( cMoveTo )
METHOD EnableDisable()
ENDCLASS
/*-------------------------------------------------------------------------------------------------*/
METHOD TBtnNav:new( oDlg, cAlias, bOnChange )
::oDlg := oDlg
::aoButtons := Array( 04 )
::abActions := Array( 04 )
AFill( ::abActions, {|| NIL } )
REDEFINE BUTTONBMP ::aoButtons[01] ID 601 OF oDlg BITMAP "BM_DTOP" ACTION ::abActions[01]
REDEFINE BUTTONBMP ::aoButtons[02] ID 602 OF oDlg BITMAP "BM_DBACK" ACTION ::abActions[02]
REDEFINE BUTTONBMP ::aoButtons[03] ID 603 OF oDlg BITMAP "BM_DNEXT" ACTION ::abActions[03]
REDEFINE BUTTONBMP ::aoButtons[04] ID 604 OF oDlg BITMAP "BM_DEND" ACTION ::abActions[04]
::aoButtons[01]:cToolTip := "Primer registro"
::aoButtons[02]:cToolTip := "Registro anterior"
::aoButtons[03]:cToolTip := "Siguiente registro"
::aoButtons[04]:cToolTip := "Ultimo registro"
IF Empty( cAlias )
IF Used()
::SetAlias( Alias() )
ENDIF
ELSE
::SetAlias( cAlias )
ENDIF
IF bOnChange <> NIL .and. ValType( bOnChange ) == "B"
::bOnChange := bOnChange
ELSE
::bOnChange := NIL
ENDIF
RETURN Self
/*-------------------------------------------------------------------------------------------------*/
METHOD TBtnNav:SetAlias( cAlias )
::cAlias := cAlias
( ::cAlias )->( DBGoTop() )
::aoButtons[ BN_TOP ]:bAction := {|| ::Navigate( "TOP" ), ::EnableDisable() }
::aoButtons[ BN_BACK ]:bAction := {|| ::Navigate( "BACK" ), ::EnableDisable() }
::aoButtons[ BN_NEXT ]:bAction := {|| ::Navigate( "NEXT" ), ::EnableDisable() }
::aoButtons[ BN_END ]:bAction := {|| ::Navigate( "END" ), ::EnableDisable() }
::EnableDisable()
RETURN NIL
/*-------------------------------------------------------------------------------------------------*/
METHOD TBtnNav:Navigate( cMoveTo )
LOCAL cNewPos, nRec
IF VALTYPE( cMoveTo ) <> "C" .OR. EMPTY( cMoveTo )
RETURN .F.
ENDIF
cMoveTo := UPPER( ALLTRIM( cMoveTo ) )
cNewPos := 'MID'
DO CASE
CASE cMoveTo = "NEXT"
( ::cAlias )->( dbskip() )
IF ( ::cAlias )->( eof() )
( ::cAlias )->( dbgobottom() )
cNewPos = "END"
ELSE
nRec = ( ::cAlias )->( recno() )
( ::cAlias )->( dbskip() )
IF ( ::cAlias )->( eof() )
cNewPos = "END"
ENDIF
( ::cAlias )->( dbgoto( nRec ) )
ENDIF
CASE cMoveTo = "BACK"
( ::cAlias )->( dbskip( -1 ) )
IF ( ::cAlias )->( bof() )
( ::cAlias )->( dbgotop() )
cNewPos = "TOP"
ELSE
nRec = ( ::cAlias )->( recno() )
( ::cAlias )->( dbskip( -1 ) )
IF ( ::cAlias )->( bof() )
cNewPos = "TOP"
ENDIF
( ::cAlias )->( dbgoto( nRec ) )
ENDIF
CASE cMoveTo = "END"
( ::cAlias )->( dbgobottom() )
cNewPos = "END"
CASE cMoveTo = "TOP"
( ::cAlias )->( dbgotop() )
cNewPos = "TOP"
OTHERWISE
RETURN .F.
ENDCASE
IF ValType( ::bOnChange ) == "B"
Eval( ::bOnChange, Self )
ENDIF
RETURN .T.
/*-------------------------------------------------------------------------------------------------*/
METHOD TBtnNav:EnableDisable()
LOCAL lOk := TRUE
LOCAL nRec, nTop, nBottom
IF Empty( ::cAlias ) .or. ( ::cAlias )->( Eof() )
::DisableAll()
RETURN FALSE
ENDIF
nRec := ( ::cAlias )->( RecNo() )
( ::cAlias )->( DBGoTop() )
nTop := ( ::cAlias )->( RecNo() )
( ::cAlias )->( DBGoBottom() )
nBottom := ( ::cAlias )->( RecNo() )
( ::cAlias )->( DBGoTo( nRec ) )
DO CASE
CASE nRec = nTop
::aoButtons[ BN_TOP ]:disable()
::aoButtons[ BN_BACK ]:disable()
::aoButtons[ BN_NEXT ]:enable()
::aoButtons[ BN_END ]:enable()
CASE nRec = nBottom
::aoButtons[ BN_TOP ]:enable()
::aoButtons[ BN_BACK ]:enable()
::aoButtons[ BN_NEXT ]:disable()
::aoButtons[ BN_END ]:disable()
OTHERWISE
::EnableAll()
ENDCASE
RETURN lOk
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
CLASS TBtnABC FROM TBtnNav
DATA lAppend
DATA lEdit
METHOD New( oDlg, cAlias, bOnChange )
METHOD SetAction( nButton, bAction, cToolTip )
METHOD SetBitmap( nButton, cBitmap, cPrompt, cTooltip ) VIRTUAL
METHOD EnableDisable2()
ENDCLASS
/*-------------------------------------------------------------------------------------------------*/
METHOD TBtnABC:New( oDlg, cAlias, bOnChange )
::lAppend := FALSE
::lEdit := FALSE
Super:New( oDlg, cAlias, bOnChange )
ASize( ::aoButtons, 10 )
ASize( ::abActions, 10 )
AFill( ::abActions, {|| NIL }, 5, 6 )
REDEFINE BUTTONBMP ::aoButtons[05] ID 605 OF oDlg BITMAP "BM_DLOCATE" ACTION ::abActions[05]
REDEFINE BUTTONBMP ::aoButtons[06] ID 606 OF oDlg BITMAP "BM_DPRINT" ACTION ::abActions[06]
REDEFINE BUTTONBMP ::aoButtons[07] ID 607 OF oDlg BITMAP "BM_DNEW" ACTION ::abActions[07]
REDEFINE BUTTONBMP ::aoButtons[08] ID 608 OF oDlg BITMAP "BM_DEDIT" ACTION ::abActions[08]
REDEFINE BUTTONBMP ::aoButtons[09] ID 609 OF oDlg BITMAP "BM_DDELETE" ACTION ::abActions[09]
REDEFINE BUTTONBMP ::aoButtons[10] ID 610 OF oDlg BITMAP "BM_DCLOSE" ACTION ::abActions[10]
::aoButtons[05]:cToolTip := "Localizar dato"
::aoButtons[06]:cToolTip := "Listar información"
::aoButtons[07]:cToolTip := "Nuevo registro"
::aoButtons[08]:cToolTip := "Editar Registro"
::aoButtons[09]:cToolTip := "Eliminar registro"
::aoButtons[10]:cToolTip := "Cerrar modulo"
::EnableDisable2()
RETURN Self
/*-------------------------------------------------------------------------------------------------*/
METHOD TBtnABC:SetAction( nButton, bAction, cToolTip )
IF nButton <= Len( ::aoButtons )
IF ValType( bAction ) == "B"
::aoButtons[ nButton ]:bAction := bAction
IF !Empty( cToolTip )
::aoButtons[ nButton ]:cToolTip := cToolTip
ENDIF
ENDIF
ENDIF
RETURN NIL
/*-------------------------------------------------------------------------------------------------*/
METHOD TBtnABC:EnableDisable2()
LOCAL lOk
lOk := Super:EnableDisable()
IF lOk
::aoButtons[ BN_LOCATE ]:enable()
::aoButtons[ BN_PRINT ]:enable()
::aoButtons[ BN_NEW ]:enable()
::aoButtons[ BN_EDIT ]:enable()
::aoButtons[ BN_DELETE ]:enable()
::aoButtons[ BN_CLOSE ]:enable()
ELSE
::aoButtons[ BN_LOCATE ]:disable()
::aoButtons[ BN_PRINT ]:disable()
::aoButtons[ BN_NEW ]:enable()
::aoButtons[ BN_EDIT ]:disable()
::aoButtons[ BN_DELETE ]:disable()
::aoButtons[ BN_CLOSE ]:enable()
ENDIF
RETURN NIL
/*-------------------------------------------------------------------------------------------------*/
test.rc
- Code: Select all Expand view RUN
TEST DIALOG 0, 0, 253, 228
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "Ms Shell Dlg"
{
PUSHBUTTON "", 601, 7, 207, 21, 14, NOT WS_TABSTOP
PUSHBUTTON "", 602, 28, 207, 21, 14, NOT WS_TABSTOP
PUSHBUTTON "", 603, 49, 207, 21, 14, NOT WS_TABSTOP
PUSHBUTTON "", 604, 70, 207, 21, 14, NOT WS_TABSTOP
PUSHBUTTON "", 605, 96, 207, 21, 14, NOT WS_TABSTOP
PUSHBUTTON "", 606, 117, 207, 21, 14, NOT WS_TABSTOP
PUSHBUTTON "", 607, 143, 207, 21, 14, NOT WS_TABSTOP
PUSHBUTTON "", 608, 164, 207, 21, 14, NOT WS_TABSTOP
PUSHBUTTON "", 609, 185, 207, 21, 14, NOT WS_TABSTOP
PUSHBUTTON "", 610, 211, 207, 21, 14, NOT WS_TABSTOP
}
BM_DBACK BITMAP ".\\tb\\back.bmp"
BM_DCLOSE BITMAP ".\\tb\\close.bmp"
BM_DDELETE BITMAP ".\\tb\\delete.bmp"
BM_DEDIT BITMAP ".\\tb\\edit.bmp"
BM_DEND BITMAP ".\\tb\\end.bmp"
BM_DLOCATE BITMAP ".\\tb\\locate.bmp"
BM_DNEW BITMAP ".\\tb\\new.bmp"
BM_DNEXT BITMAP ".\\tb\\next.bmp"
BM_DPRINT BITMAP ".\\tb\\print.bmp"
BM_DSAVE BITMAP ".\\tb\\save.bmp"
BM_DTOP BITMAP ".\\tb\\top.bmp"
BM_DUNDO BITMAP ".\\tb\\undo.bmp"
Pero de momento existe algunas cuestiones a resolver.
1.- no es un control, lo cual me gustaria. (yo he hecho mis pinitos crendo clases tales como TTitle, xBackupLite, etc., pero no con controles que interactuen con el usuario, o que contenga otros controles)
2.- el metodo que determina e estado activado o desactivado de los botones de navegacion, tiene un codigo no optimizado para la modalidad cliente/servidor ya que usa saltos al inicio y al final para determinar la posicion del puntero en la tabla asociada, esto en cada interaccion de los botones, esto funciona bien en modo local, pero lo veo ineficiente en c/s. ( lo curioso es que hae tiempo habia hecho un codigo para esto ms optimizado y lo perdi )
3- en la segunda clase TBtnABC el metodo EnableDisable2 lo queria definir como en la clase superior TBtnNav que se llamara EnableDisable, pero me fallo cuando hice uso de TBtnABC, ya que me llamaba al metodo EnableDisable de la clase TBtnABC.
Si alguien se anima a ayudarme bienvenido sea, ya que creo firmemente que necesitamos algo como esto, para ir cambiando el chip de que para todo es con browses.
Nota: no estoy en contra de los browse, es mas, fue lo que me llamo la atencion en clipper desde un inicio, pero los tiempos han cambiado, y segun leo no es recomendable en c/s.
por algo se inicia, y yo pues doy el primer paso.
agradeciendo su atencion, y su ayuda.
salu2
carlos vargas