Cerrar un dialogo

Cerrar un dialogo

Postby antolin » Fri May 29, 2015 12:03 pm

Hola foreros

Repasando algunos asuntos que tenía pendientes, he notado que en este foro ha salido, en distintas ocasiones, el tema de cerrar un dialogo cuando pulsamos fuera con el ratón, sin que se le haya dado una solución genérica. Bueno pues quiero exponer aquí algunas soluciones que he aplicado a mis programas y que me han ido muy bien.

Como podrá imaginar todo depende del tipo de dialogo y de la cantidad de controles que tenga el dialogo; lo que sí es absolutamente necesario es que sea no modal (NOWAIT). Aun así he probado distintas soluciones, unas me han ido mejor que otras. Principalmente utilizo tres métodos, todos efectivos (al menos en mis programas):

1º Tengo un dialogo NOWAIT con un solo control. Por ejemplo un Listbox que ocupa todo el dialogo. Solución:
Code: Select all  Expand view  RUN
DEFINE DIALOG oDlg ….
   REDEFINE LISTBOX oLbx …
ACTIVATE DIALOG oDlg NOWAIT
*
oLbx:bLostFocus = { || oDlg:End() }
 

Con la única precaución de asegurarse de que el Listbox toma el foco siempre que se activa el dialogo. Por ejemplo comprobando que el nStyle del Listbox contiene la directiva WS_TABSTOP.

2º Tengo un dialogo con dos o tres controles. Por ejemplo un Listbox y dos botones. Es muy común. Mi solución:
Code: Select all  Expand view  RUN
LOCAL oCtrl[3]
*
DEFINE DIALOG oDlg ….
   REDEFINE LISTBOX oCtrl[1]
   REDEFINE BUTTON oCtrl[2]
   REDEFINE BUTTON oCtrl[3]
ACTIVATE DIALOG oDlg NOWAIT
*
AEVAL( oCtrl, { |oCt|  oCt:bLostFocus := { || IF(GetActiveWindow() # oDlg:hWnd,oDlg:End(),) } } )
 

Precauciones:
- Que al menos uno de los controles tome el foco al activarse el dialogo.
- Definir todos los controles un en solo array, en este caso oCtrl[3] para manejarlos con AEVAL(), ahorra tener que definir un bLostFocus para cada control, aunque tampoco pasa nada pues sólo son tres controles.

También podría dirigir todos los bLostFocus hacia una función que controle el cierre del dialogo:
AEVAL( oCtrl, { |oCt| oCt:bLostFocus := { |Self| FuncEnd( Self,…) } } )

¿Ha notado que, en ambos casos, he definido los bLostFocus después de activar el dialogo? Es importante, pero no necesario, aunque aconsejo que lo haga así, especialmente cuando tiene que definir un ON INIT para el dialogo.

Por otra parte, he de señalar que muchas veces el bLostFocus interfiere con las acciones a las que llaman los distintos controles. Por ejemplo desplegar un MessageBox al pulsar uno de los botones, en cuyo caso se activaría el bLostFocus y se cerraría el dialogo inoportunamente. No es problema, en la función que llama al MessageBox guarde el contenido del bLostFocus en una variable, ponga el bLostFocus a NIL y recupérelo después de la acción, al final de la función.
Code: Select all  Expand view  RUN
FUNCTION MiFunc()
   LOCAL bLost := oCtrl: bLostFocus
   *
   bLostFocus := NIL
   …
   MSGALERT()
   …
   bLostFocus := bLost
RETURN

O

FUNCTION MiFunc()
   LOCAL bLost := oCtrl[1]: bLostFocus
   *
   AEVAL(oDlg:aControls,{ |o| o:bLostFocus := NIL })
   …
   MSGALERT()
   …
   AEVAL(oDlg:aControls,{ |o| o:bLostFocus := bLost })
RETURN

 

3º Tengo un dialogo NOWAIT complejo, con controles de todo tipo, y el dialogo tiene definidas clausulas ON INIT, VALID, etc y quiero que se cierre cuando pulso fuera con el ratón, sin renunciar al comportamiento normal del dialogo, todos sus controles, sus cláusulas y codeblocks.

Esta solución implica modificar ligeramente la clase TWINDOW, o en su defecto la TDIALOG, pero merece la pena, pues el programa hace exactamente lo que queremos que haga. En cuanto pulsas fuera del dialogo este ejecuta su VALID y se cierra (si el VALID devuelvió .T. claro)

1) Hay que añadir un data, el que recoje el codeblock que deberá ejecutarse cuando pulse fuera del dialogo. Yo lo he llamado ‘bNcLost’, pero podeis darle el nombre que más os guste

- Hay que modificar el METHOD NcActivate() de TWINDOWS de la siguiente manera (o podeis crearlo en TDIALOG):
Code: Select all  Expand view  RUN
METHOD NcActivate( lOnOff )     CLASS TDIalog
   LOCAL hFWnd := GetFocus()
   *
   IF !lOnOff .AND. ::hWnd # hFWnd
      IF ::bNcLost # NIL
         Eval( ::bNcLost, Self, hFWnd)
      ELSEIF ::bLostFocus # NIL
         Eval( ::bLostFocus, Self, hFWnd )
      ENDIF
   ENDIF
RETURN NIL
 

Si analizáis el METHOD comprobará que varía muy poquito respecto del original, aunque no lo parezca.

- ahora, en vuestro programa, definid vuestro dialogo de la siguiente manera:
Code: Select all  Expand view  RUN
DEFINE DIALOG oDlg ….
   REDEFINE LISTBOX oLbx …
   REDEFINE SAY oSay1 …
   REDEFINE SAY oSay2 …
   …
   REDEFINE BUTTON oBot1 …
   REDEFINE BUTTON oBot2 …
   …
   oDlg: bPainted  := { |hdc| …}
   oDlg: bRClicked := { ||  … }
  …

oDlG:bNcLost   := { |oDiag| PostMessage(oDiag:hWnd,WM_CLOSE) }
o
oDlG:bNcLost   := { |oDiag| FuncClose( oDiag,…) }

  …
ACTIVATE DIALOG oDlg NOWAITON INITVALID
 

En este caso no es necesario definir bNcLost después de activar el dialogo.

Espero os sea de utilidad.

Saludos.
Peaaaaaso de foro...
FWH 2007 - xHarbour - BCC55
antolin
 
Posts: 498
Joined: Thu May 10, 2007 8:30 pm
Location: Sevilla

Re: Cerrar un dialogo

Postby hmpaquito » Fri May 29, 2015 12:27 pm

Magnifico post Antolín.

Antonio Linares puso un post para cerrar un dialog. Utilizaba la tecnica de capturar el mouse para el dialogo. Cualquier clicking fuera de las dimensiones del dialogo provocaba el cierre de este. No lo llegue a probar pero la idea me parecio muy buena: el proceso era limpio y rapido.

De tus metodos, el que parece que me gusta mas es el ultimo. Creo que fwh deberia incorporar de serie un sistema que hiciera esto, especialmente pensando en los sistemas tactiles. Quiza se podria aprovechar tu sistema: me parece limpio y claro.
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: Cerrar un dialogo

Postby antolin » Fri May 29, 2015 3:26 pm

La verdad es que, ahora, mirando mi post, me he dado cuenta de un pequeño detalle. Parece como si bNcLost hiciera lo mismo que bLostFocus, sin embargo he de decir, en honor a la verdad, que yo no he modificado el METHDO de TWINDOWS, pues tengo un TDIALOG modificado y en esa clase he añadido el METHOD NcActivate (sin la parte de bLostFocus). En teoría es lo mismo puesto que TDIALOG hereda de TWINDOWS, pero lo que si es cierto es que con bLostFocus de TWINDOS no ocurre el efecto que logro con el bNcLost de mi TDIALOG y no se porqué.

Un saludo
Peaaaaaso de foro...
FWH 2007 - xHarbour - BCC55
antolin
 
Posts: 498
Joined: Thu May 10, 2007 8:30 pm
Location: Sevilla

Re: Cerrar un dialogo

Postby horacio » Fri May 29, 2015 4:58 pm

Colegas, aquí pongo un ejemplo donde nunca se llega a abrir el dialogo, ya que no hace nunca foco.

Code: Select all  Expand view  RUN
#include 'fivewin.ch'

//----------------------------------------------------------------------------//
Function Main()

    Local oGet
    Local oDlg
    Local cVar := Space( 60 )
   
    Define Dialog oDlg From 100, 100 To 400, 500 Pixel
   
        @ 20, 10 Get oGet Var cVar Of oDlg Size 100, 12 Picture '@!' Action AbreCaja( oGet, oDlg ) Bitmap FWDArrow() Pixel
       
    Activate Dialog oDlg
    Return 0

//----------------------------------------------------------------------------//
Function AbreCaja( oGet, oDialogo )

    Local oDlg
    Local aPoint := { oGet : nTop() - 80, oGet : nLeft() }
   
    ClientToScreen( oDialogo : hWnd, aPoint )

    Define Dialog oDlg Of oDialogo From aPoint[ 1 ] + 110, aPoint[ 2 ] To aPoint[ 1 ] + 180, aPoint[ 2 ] + 180 Pixel Style nOr( WS_POPUP, WS_BORDER ) Color 0, CLR_WHITE

        oDlg : bLostFocus := { || oDlg : End() }
       
    Activate Dialog oDlg On Init oDlg : SetFocus() NoWait
    Return 0    
 


Alguna solución para este problema ? Muchísimas gracias

Saludos
horacio
 
Posts: 1363
Joined: Wed Jun 21, 2006 12:39 am
Location: Capital Federal Argentina

Re: Cerrar un dialogo

Postby hmpaquito » Fri May 29, 2015 5:09 pm

horacio,

Si el problema es que no hay ningun control definido, yo definiria un get asi:

@ 3000,3000 GET cValor // Workaround

Esta solucion tambien es valida cuando hay que validar el ultimo get, pero no se valida porque no pierde el foco porque no hay control después.

Saludos
hmpaquito
 
Posts: 1482
Joined: Thu Oct 30, 2008 2:37 pm

Re: Cerrar un dialogo

Postby horacio » Fri May 29, 2015 5:16 pm

Antolín, aunque le ponga un control tampoco llega a hacer foco. Me he estado peleando con esto hace tiempo. Lo que noté es que no ocurre en una window con un dialog NoWait, con esa configuración si funciona.

Saludos
horacio
 
Posts: 1363
Joined: Wed Jun 21, 2006 12:39 am
Location: Capital Federal Argentina

Re: Cerrar un dialogo

Postby horacio » Fri May 29, 2015 5:24 pm

Un ejemplo con un control en el dialogo que quiero abrir

Code: Select all  Expand view  RUN
#include 'fivewin.ch'

//----------------------------------------------------------------------------//
Function Main()

    Local oGet
    Local oDlg
    Local cVar := Space( 60 )
   
    Define Dialog oDlg From 100, 100 To 400, 500 Pixel
   
        @ 20, 10 Get oGet Var cVar Of oDlg Size 100, 12 Picture '@!' Action AbreCaja( oGet, oDlg ) Bitmap FWDArrow() Pixel
       
    Activate Dialog oDlg
    Return 0

//----------------------------------------------------------------------------//
Function AbreCaja( oGet, oDialogo )

    Local oDlg
    Local aDatos := { 'UNO', 'DOS', 'TRES' }
    Local aPoint := { oGet : nTop() - 80, oGet : nLeft() }
   
    ClientToScreen( oDialogo : hWnd, aPoint )

    Define Dialog oDlg Of oDialogo From aPoint[ 1 ] + 110, aPoint[ 2 ] To aPoint[ 1 ] + 180, aPoint[ 2 ] + 180 Pixel Style nOr( WS_POPUP, WS_BORDER ) Color 0, CLR_WHITE

       
        @ 0, 0 xBrowse oBrw DataSource aDatos Columns 1 Size 92, 35 Pixel NoBorder
        oBrw : CreateFromCode()    
       
        oDlg : bLostFocus := { || oDlg : End() }
       
    Activate Dialog oDlg On Init oBrw : SetFocus() NoWait
    Return 0    
 


Saludos
horacio
 
Posts: 1363
Joined: Wed Jun 21, 2006 12:39 am
Location: Capital Federal Argentina

Re: Cerrar un dialogo

Postby antolin » Mon Jun 01, 2015 8:30 am

Horacio, si no hay controles, no pueden tomar o perder el foco... La única solución que a mi me funciona es la tercera vía que he explicado en mi post: 'bNcLost', significa modificar TDIALOG, pero merece la pena, funciona muy bien.
Peaaaaaso de foro...
FWH 2007 - xHarbour - BCC55
antolin
 
Posts: 498
Joined: Thu May 10, 2007 8:30 pm
Location: Sevilla

Re: Cerrar un dialogo

Postby antolin » Mon Jun 01, 2015 8:38 am

En cuanto al xBrowse, Horacio, no sé, hace muchos años que no lo utilizo. Asegúrate de que tiene definido el WS_TABSTOP en su nStyle y que el dialogo permite que el xBrowse tome el foco. También asegúrate de que no le has definido el bLostFocus por otro lado. Pero primero, prueba a poner bLostFocus fuera de la estructura DEFINE DIALOG...ACTIVATE, es decir después del ACTIVATE, a veces me ha pasado. Si no, prueba con la segunda fórmula:

oBrw:bLostFocus := { || IF(GetActiveWindow() # oDlg:hWnd,oDlg:End(),) }

Espero te sirva. Un saludo
Peaaaaaso de foro...
FWH 2007 - xHarbour - BCC55
antolin
 
Posts: 498
Joined: Thu May 10, 2007 8:30 pm
Location: Sevilla

Re: Cerrar un dialogo

Postby Antonio Linares » Mon Jun 01, 2015 8:53 am

Horacio,

Yo lo hago asi:

Code: Select all  Expand view  RUN
function PopupBrowse( aValue, oGet, bInit )

   local oDlg, oBrw
   local aPoint := { oGet:nTop + oGet:nHeight, oGet:nLeft } 

   if oGet:Cargo == nil
      aPoint = ClientToScreen( oGet:oWnd:hWnd, aPoint )
    
      DEFINE DIALOG oDlg OF oGet:oWnd STYLE WS_POPUP ;
         SIZE 500, 180     
     
      ACTIVATE DIALOG oDlg NOWAIT ;
         ON INIT oDlg:SetPos( aPoint[ 1 ], aPoint[ 2 ] )
    
      @ 0, 0 XBROWSE oBrw ARRAY aValue AUTOSORT ;
         SIZE oDlg:nWidth, oDlg:nHeight OF oDlg PIXEL
         
      oBrw:CreateFromCode()    
           
      Eval( bInit, oBrw )                  

      oBrw:PostMsg( WM_SETFOCUS )
      oBrw:bKeyDown = { | nKey | If( nKey == VK_RETURN, oDlg:End(),) }     
      oBrw:bChange = { || oGet:VarPut( oBrw:aCols[ 1 ]:Value ), oGet:Refresh() }       
      oBrw:bLButtonUp = { | nRow, nCol | If( nRow > oBrw:nHeaderHeight,;
                          ( Eval( oBrw:bChange ), oDlg:End(),;
                            oGet:oWnd:GoNextCtrl( oGet:hWnd ),;
                            oGet:Cargo := nil ),) }
                             
      oBrw:Seek( oGet:GetText() )                                                            

      oGet:Cargo = oDlg    
      oGet:bKeyDown = { | nKey | If( nKey == VK_DOWN, oBrw:GoDown(),),;
                                 If( nKey == VK_UP, oBrw:GoUp(),),;
                                 If( nKey == VK_RETURN, oDlg:End(),), 0 }
                                 
      oGet:oWnd:bLClicked = { || oDlg:End(), oGet:Cargo := nil }                                                                   
      oGet:oWnd:bMouseWheel = { || oDlg:SetFocus() }
   else
      oGet:Cargo:End()
      oGet:Cargo = nil     
   endif       
           
return nil               
regards, saludos

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

Re: Cerrar un dialogo

Postby antolin » Mon Jun 01, 2015 9:12 am

Exacto, Horacio, se trata de que tu xBrowse tome el foco para poder decirle que cierre el dialogo cuando lo pierda. Antonio lo solventa muy sabiamente con oBrw:PostMsg( WM_SETFOCUS ). Lo que no veo tan claro es:
Code: Select all  Expand view  RUN
     oGet:oWnd:bLClicked = { || oDlg:End(), oGet:Cargo := nil }                                                                    
      oGet:oWnd:bMouseWheel = { || oDlg:SetFocus() }
 

¿Que pasa si pincho fuera del oGet:oWnd?, y por otro lado, porque hay que decirle al xBrowse que retome el foco a cada MouseWheel. No sería más efectivo que no pierda el foco con la rueda del ratón, porqué habría de perderlo? Todo desde el mayor de los respetos por el maestro claro.
Peaaaaaso de foro...
FWH 2007 - xHarbour - BCC55
antolin
 
Posts: 498
Joined: Thu May 10, 2007 8:30 pm
Location: Sevilla

Re: Cerrar un dialogo

Postby antolin » Mon Jun 01, 2015 9:14 am

Perdón, he confundido oGet:oWnd:bMouseWheel con oBrw:bMouseWheel. Mejor será que me calle a ese rescpecto... Perdón de nuevo
Peaaaaaso de foro...
FWH 2007 - xHarbour - BCC55
antolin
 
Posts: 498
Joined: Thu May 10, 2007 8:30 pm
Location: Sevilla


Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: cmsoft and 55 guests