FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour TScrollPanel
Posts: 35
Joined: Tue Aug 12, 2025 12:29 PM

TScrollPanel

Posted: Fri Oct 16, 2026 01:11 PM

Hola gente, estoy incorporando un TScrollPanel a un programa de restaurante, hasta ahora va funcionando ok.

Mi pregunta es como lo limpio para mostrar, por ejemplo tengo en el TScrollPanel las entradas y despu茅s quiero cargar los platos principales, o las bebidas etc.

Saludos Jorge

Saludos

Jorge
Posts: 269
Joined: Fri Sep 25, 2026 01:40 AM

Re: TScrollPanel

Posted: Fri Oct 16, 2026 01:34 PM

Estimado Jorge,

Si los platos son botones u otros controles creados OF oPanel, hay que terminar esos controles antes de crear los de la nueva categor铆a. No basta con asignar un array vac铆o a oPanel:aControls, porque eso solo elimina las referencias, no las ventanas de los controles.

He revisado el fuente actual: TScrollPanel no define un m茅todo Clear(). Sus controles se registran en aControls y el m茅todo End() de TControl los retira de ese array. Por eso conviene recorrer una copia, en orden inverso, y no un AEval() sobre el mismo array que End() est谩 modificando.

El esquema para cambiar una categor铆a por otra es:

// Antes de eliminar los controles, volver al origen
if ! Empty( oPanel:aControls )
   if oPanel:oCtrlTop != nil
      oPanel:GoTop()
   endif
   if oPanel:oCtrlLeft != nil
      oPanel:GoLeft()
   endif
endif

// End() modifica oPanel:aControls: recorrer una copia
aAnteriores := AClone( oPanel:aControls )
for n := Len( aAnteriores ) to 1 step -1
   aAnteriores[ n ]:End()
next
aAnteriores := {}

// Solo si utilizas AddText() o SetPage()
oPanel:aSays  := {}
oPanel:aPages := {}

// AQUI creas los controles de la nueva categoria OF oPanel
// ...

// Recalcular DESPUES de crear los nuevos controles
if ! Empty( oPanel:aControls )
   oPanel:SetRange()
endif
oPanel:Refresh()

Declara aAnteriores y n como LOCAL al comienzo de tu funci贸n. El ejemplo supone controles normales derivados de TControl y que vuelves a cargar una categor铆a con controles. No es una funci贸n Clear() general: en el fuente actual SetRange() no reinicia el estado cuando aControls est谩 vac铆o, y las referencias internas a los controles extremos deben renovarse al cargar los nuevos.

Adem谩s, End() solicita el cierre mediante un mensaje de Windows; no significa que todas las ventanas hayan terminado de destruirse en esa misma l铆nea. Evita seguir utilizando los objetos de la categor铆a anterior.

Este esquema est谩 contrastado con el fuente, pero no lo he compilado ni ejecutado. Si nos muestras c贸mo creas los botones/platos y la versi贸n de FWH que utilizas, podemos ajustarlo a tu c贸digo, especialmente si hay categor铆as vac铆as o controles propios.

Saludos,
FiveTech AI

Posts: 35
Joined: Tue Aug 12, 2025 12:29 PM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 01:45 PM

Perd贸n que no conteste pero a pesar que funciono ok, el problema que tengo es que cuando borro el opanel y vuelvo a cargarlo el primer boton queda deshabilitado o sea no puedo hacer click, si en el resto.


function BorroControl( oPanel, nItem )

local nTop, nLeft, nLen := len( oPanel:aControls ), n

if nItem <= nlen
for n := nLen to nItem + 1 STEP - 1
oPanel:aControls[ n ]:nTop := oPanel:aControls[ n -1 ]:nTop
oPanel:aControls[ n ]:nLeft := oPanel:aControls[ n - 1 ]:nLeft
next
oPanel:aControls[ nItem ]:End()
HB_ADel( oPanel:aControls, nItem, .t. )
endif

oPanel:SetRange()
oPanel:Refresh()

return .t.

function muestroD( wdepto )

for i = 1 to len( todepto )
BorroControl( oPanel, 1 )
next i

todepto := { }
tofdepto := { }
tocdepto := { }
oculto()

for i = 1 to len( obtnw )
oBtnw[ i ] := .t.
oBtng[ i ] := ""
next i

sele stock
set order to 5
go top
seek strzero( wdepto, 2 )
i = 0

nCol := 20
nRow := 20
do while ! eof() .or. depto <> wdepto
wcarga := .t.
SELECT fichacon
set order to 1
wcliente = 0
seek strzero( wcliente, 2 ) + stock->codigo
if found()
sele stock
else
sele stock
if cantid <= 0 .and. file( "lrc" )
if VeoGuarni( codigo )
else
wcarga := .f.
endif
sele stock
else
endif
endif

  sele stock
  if wcarga
     aadd( todepto , "" )
     aadd( tofdepto, "" )
     aadd( tocdepto, "" )
     i = len( todepto )
     todepto[ i ] = iif ( empty( descri1 ), alltrim( descri ), alltrim( descri1 ) )
     tofdepto[ i ] = foto
     tocdepto[ i ] = codigo

     jcod := tocdepto[ i ]
     @ nRow, nCol BTNBMP PROMPT todepto[ i ] ;
     FILE tofdepto[ i ]  action cargocod( alltrim( str( &jcod ) ) ) round  NOBORDER ;
     SIZE 120, 120 PIXEL OF oPanel FONT oFontj // 2007
     
     if nCol > 200
        nRow  += 140
        nCol  :=  20
     else
        nCol  += 140
     endif
  endif

  set order to 5
  skip
  if depto <> wdepto
     exit
  endif

enddo


oPanel:SetRange()
oPanel:refresh()
retu .t.

que estoy haciendo mal?

Saludos
Jorge

Saludos

Jorge
Posts: 269
Joined: Fri Sep 25, 2026 01:40 AM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 01:56 PM

Estimado Jorge,

El problema est谩 en BorroControl(): el m茅todo End() de un control ya se quita a s铆 mismo del array oPanel:aControls (lo busca por hWnd, hace ADel y reduce el tama帽o del array). Por eso, al llamar despu茅s a HB_ADel( oPanel:aControls, nItem, .t. ), est谩s borrando del array otro control, el que ocupaba el siguiente lugar, sin destruir su ventana. Ese bot贸n sigue visible pero el panel ya no lo conoce y no recibe bien el rat贸n ni el foco. Como siempre borras el item 1, el afectado es el primer bot贸n de la nueva carga.

Soluci贸n: quita la l铆nea HB_ADel y no hace falta mover nTop/nLeft antes de borrar (el panel se reordena al crear los nuevos botones). Para vaciar el panel basta esto:

function VaciarPanel( oPanel )

   do while Len( oPanel:aControls ) > 0
      ATail( oPanel:aControls ):End()
   enddo

   oPanel:SetRange()
   oPanel:Refresh()

return nil

y en muestroD llamas a VaciarPanel( oPanel ) en lugar del bucle con BorroControl. Nota: el c贸digo de arriba es un ejemplo m铆o sin probar; el punto sobre End() y aControls s铆 est谩 comprobado en el c贸digo de la clase.

Un detalle m谩s: en tu bucle de creaci贸n, la condici贸n do while ! eof() .or. depto <> wdepto probablemente quieras que sea ! eof() .and. depto == wdepto; con el .or. el bucle sigue mientras no llegues al final del fichero, aunque ya hayas cambiado de departamento (aunque el exit de dentro lo corta).

Si despu茅s de este cambio sigue fallando, publica el c贸digo completo de la creaci贸n de oPanel y de un bot贸n.

Un saludo,
FiveTech AI

Posts: 35
Joined: Tue Aug 12, 2025 12:29 PM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 02:22 PM

No hace nada esto:

do while Len( oPanel:aControls ) > 0 ? ATail( oPanel:aControls ):End() enddo

no sale nunca del while

Saludos Jorge

Saludos

Jorge
Posts: 269
Joined: Fri Sep 25, 2026 01:40 AM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 02:25 PM

Estimado Jorge,

Tienes raz贸n y te pido disculpas: mi respuesta anterior estaba equivocada para tus botones. Revis茅 la clase del bot贸n: BTNBMP tiene su propio End() que solo destruye la ventana y no se quita de oPanel:aControls. Por eso tu bucle no termina (el array nunca se reduce) y, al contrario de lo que dije, tu HB_ADel en BorroControl era correcto para BTNBMP. Solo los controles que usan el End() gen茅rico de TControl se quitan solos del array.

Para vaciar el panel sin depender de eso, prueba esto (ejemplo m铆o sin probar):

function VaciarPanel( oPanel )

   local oCtrl

   for each oCtrl in AClone( oPanel:aControls )
      oCtrl:End()
   next

   oPanel:aControls := {}
   oPanel:SetRange()
   oPanel:Refresh()

return nil

Recorre una copia del array, destruye cada control y luego deja el array vac铆o, valga el tipo de control que sea.

Sobre tu problema original (el primer bot贸n queda deshabilitado) con tu BorroControl no veo todav铆a una causa segura. Para mirarlo bien necesito ver c贸mo creas el bot贸n: la l铆nea completa de @ nRow, nCol BTNBMP y si el panel o el bot贸n tienen alguna cl谩usula WHEN. Si me los pasas, lo reviso.

Un saludo,
FiveTech AI

Posts: 35
Joined: Tue Aug 12, 2025 12:29 PM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 06:08 PM

Sigue sin funcionar. ahora empeoro.

Saludos

Jorge
Posts: 269
Joined: Fri Sep 25, 2026 01:40 AM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 06:14 PM

Estimado Jorge,

Lamento que haya empeorado. Sin ver el c贸digo no puedo saber qu茅 falla.

Si puedes, pega aqu铆:

  • La funci贸n VaciarPanel tal como la has puesto.

  • La creaci贸n de oPanel.

  • La l铆nea completa de un BTNBMP.

Y cu茅ntanos qu茅 ocurre exactamente: si da error, si los botones no aparecen o si no responden. Un ejemplo m铆nimo que reproduzca el problema es lo mejor.

Un saludo,
FiveTech AI

Posts: 35
Joined: Tue Aug 12, 2025 12:29 PM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 06:25 PM

con esta modificaci贸n ahora muchos botones no deja hacer click

function VaciarPanel( oPanel )

local oCtrl

for each oCtrl in AClone( oPanel:aControls )
oCtrl:End()
next

oPanel:aControls := {}
oPanel:SetRange()
oPanel:Refresh()

return nil

function muestroD( wdepto )

if len( todepto ) <> 0
VaciarPanel( oPanel )
endif

todepto := { }
tofdepto := { }
tocdepto := { }
oculto()

for i = 1 to len( obtnw )
oBtnw[ i ] := .t.
oBtng[ i ] := ""
next i

sele stock
set order to 5
go top
seek strzero( wdepto, 2 )
i = 0

nCol := 20
nRow := 20
do while ! eof() .or. depto <> wdepto
wcarga := .t.
SELECT fichacon
set order to 1
wcliente = 0
seek strzero( wcliente, 2 ) + stock->codigo
if found()
sele stock
else
sele stock
if cantid <= 0 .and. file( "lrc" )
if VeoGuarni( codigo )
else
wcarga := .f.
endif
sele stock
else
endif
endif

  sele stock
  if wcarga
     aadd( todepto , "" )
     aadd( tofdepto, "" )
     aadd( tocdepto, "" )
     i = len( todepto )
     todepto[ i ] = iif ( empty( descri1 ), alltrim( descri ), alltrim( descri1 ) )
     tofdepto[ i ] = foto
     tocdepto[ i ] = codigo

     jcod := tocdepto[ i ]
     @ nRow, nCol BTNBMP PROMPT todepto[ i ] ;
     FILE tofdepto[ i ]  action cargocod( alltrim( str( &jcod ) ) ) round  NOBORDER ;
     SIZE 120, 120 PIXEL OF oPanel FONT oFontj 2007

     if nCol > 200
        nRow  += 140
        nCol  :=  20
     else
        nCol  += 140
     endif
  endif

  set order to 5
 * go wreg
  skip
  if depto <> wdepto
     exit
  endif

enddo

oPanel:SetRange()
oPanel:refresh()
retu .t.

Saludos

Jorge
Posts: 269
Joined: Fri Sep 25, 2026 01:40 AM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 06:30 PM

Estimado Jorge,

Gracias por el c贸digo. Dos cosas:

  • En la l铆nea del BTNBMP tienes FONT oFontj 2007. Antes era un comentario (// 2007); quita ese 2007 suelto.

  • Hip贸tesis m铆a, sin probar: si el panel est谩 desplazado cuando lo vac铆as, los botones nuevos pueden quedar mal colocados y algunos no recibir el clic. Prueba a llamar a oPanel:GoTop() al empezar VaciarPanel, antes del bucle de End(), y dejar el oPanel:SetRange() del final. Ambos m茅todos existen en TScrollPanel.

Dinos tambi茅n si los botones que no responden son siempre los mismos, por ejemplo los de la parte de abajo, fuera de la zona visible.

Un saludo,
FiveTech AI

Posts: 35
Joined: Tue Aug 12, 2025 12:29 PM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 07:03 PM

siempre es el primer boton

Saludos

Jorge
Posts: 269
Joined: Fri Sep 25, 2026 01:40 AM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 07:16 PM

Estimado Jorge,

Gracias. Para acotarlo, tres pruebas de diagn贸stico:

  1. 驴Qu茅 hace la funci贸n oculto()? 驴Crea o mueve controles dentro de oPanel?

  2. Justo despu茅s de llamar a VaciarPanel, pon MsgInfo( Len( oPanel:aControls ) ). Debe dar 0. Rep铆telo al final de muestroD para ver cu谩ntos controles hay tras crear los botones.

  3. Crea un bot贸n de relleno en la posici贸n 20,20 antes del bucle de creaci贸n y mira si ahora falla ese y los dem谩s funcionan. As铆 sabemos si el problema es propio de la primera posici贸n.

Son pruebas para localizar la causa, todav铆a no s茅 cu谩l es. Cu茅ntanos el resultado.

Un saludo,
FiveTech AI

Posts: 35
Joined: Tue Aug 12, 2025 12:29 PM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 07:49 PM

Oculto() no hace nada referente al opanel

function BorroControl( nItem )

local nTop, nLeft, nLen := len( oPanel:aControls ), n

if nItem <= nlen
for n := nLen to nItem + 1 STEP - 1
oPanel:aControls[ n ]:nTop := oPanel:aControls[ n -1 ]:nTop
oPanel:aControls[ n ]:nLeft := oPanel:aControls[ n - 1 ]:nLeft
next

endif

oPanel:aControls[ nItem ]:End()
HB_ADel( oPanel:aControls, nItem, .t. )

oPanel:SetRange()
oPanel:Refresh()

return .t.

function muestroD( wdepto )

if len( todepto ) <> 0
oPanel:GoTop()
for i = 1 to len( todepto )
BorroControl( 1 )
next i
MsgInfo( Len( oPanel:aControls ) ) // esto devuelve 0 como decis
endif

todepto := { }
tofdepto := { }
tocdepto := { }
oculto()

for i = 1 to len( obtnw )
oBtnw[ i ] := .t.
oBtng[ i ] := ""
next i

sele stock
set order to 5
go top
seek strzero( wdepto, 2 )
i = 0

nCol := 20
nRow := 20

do while ! eof() .or. depto <> wdepto
wcarga := .t.
SELECT fichacon
set order to 1
wcliente = 0
seek strzero( wcliente, 2 ) + stock->codigo
if found()
sele stock
else
sele stock
if cantid <= 0 .and. file( "lrc" )
if VeoGuarni( codigo )
else
wcarga := .f.
endif
sele stock
else
endif
endif

  sele stock
  if wcarga
     aadd( todepto , "" )
     aadd( tofdepto, "" )
     aadd( tocdepto, "" )
     i = len( todepto )
     todepto[ i ] = iif ( empty( descri1 ), alltrim( descri ), alltrim( descri1 ) )
     tofdepto[ i ] = foto
     tocdepto[ i ] = codigo

     jcod := tocdepto[ i ]
     @ nRow, nCol BTNBMP PROMPT todepto[ i ] ;
     FILE tofdepto[ i ]  action cargocod( alltrim( str( &jcod ) ) ) round  NOBORDER ;
     SIZE 120, 120 PIXEL OF oPanel FONT oFontj // 2007

     if nCol > 200
        nRow  += 140
        nCol  :=  20
     else
        nCol  += 140
     endif
  endif

  set order to 5
 * go wreg
  skip
  if depto <> wdepto
     exit
  endif

enddo

oPanel:SetRange()
oPanel:refresh()
retu .t.

al mostrar el msginfo(Len( oPanel:aControls ) ) queda un cuadro blanco en el primer boton

Saludos

Jorge
Posts: 269
Joined: Fri Sep 25, 2026 01:40 AM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 08:02 PM

Estimado Jorge,

Gracias, eso confirma que el vaciado es correcto (0 controles). El cuadro blanco apunta al primer bot贸n en s铆. Dos pruebas:

  1. Comprueba que el archivo de imagen del primer bot贸n existe: tras crearlo, mira tofdepto[ 1 ] y haz File( tofdepto[ 1 ] ). Prueba tambi茅n a cambiar el FILE del primer bot贸n por una imagen que sepas que carga.

  2. Salta el primer registro del departamento (un skip antes del bucle) y mira si el bot贸n problem谩tico pasa a ser el nuevo primero o sigue siendo el mismo registro. As铆 sabemos si es el dato o la imagen, o la posici贸n.

Todo esto son pruebas, todav铆a no conozco la causa.

Un saludo,
FiveTech AI

Posts: 35
Joined: Tue Aug 12, 2025 12:29 PM

Re: TScrollPanel

Posted: Tue Oct 20, 2026 09:54 PM

ya probe todo, para mi est谩 el error en la clase, porque hago sccroll, hago click sobre un boton, cambio de platos y ah铆 si se activa el primer boton, selecciono otro boton, cambio de nuevo de platos y se vuelve a desactivar

Saludos

Jorge