Page 1 of 1

Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 7:09 am
by triumvirato
El comportamiento de un boton es distinto cuando lo pulsas con ratón que cuando lo pulsas con la tecla Enter. El comportamiento que ha de hacer en _, incluye un SetFocus() en su cláusula ACTION que sí se respeta cuando pulsas con ratón sobre el boton, y se lo salta cuando oprimes sobre el la tecla Enter, pero si obedece en _ al resto de cláusulas.
Un ejemplo a ver si me sabe alguien ayudar, Gracias.

Code: Select all

#include "Fivewin.ch"


function Main()

   local oDlg_GenAut, oBtnAnade, oBrowPed,;
         aValues := { { "One", "Two", "Three" }, { "Four", "Five", "Six" } },;
         oGetCodArt, oGetUdsPed, cCod := Space( 14 ), nCan := 0, nRGBColor := nRGB( 0,255,255 )
         
   SetGetColorFocus( nRGBColor )

   DEFINE DIALOG oDlg_GenAut RESOURCE "DLG_GENAUT"

   REDEFINE BUTTON oBtnAnade ID 4050 OF oDlg_GenAut UPDATE ACTION ( oGetCodArt:Enable(), oGetUdsPed:Enable(), oGetCodArt:SetFocus() )
   
   REDEFINE LISTBOX oBrowPed;
      FIELDS aValues[ oBrowPed:nAt, 1 ], aValues[ oBrowPed:nAt, 2 ], aValues[ oBrowPed:nAt, 3 ];
       ID 90 OF oDlg_GenAut UPDATE
      oBrowPed:SetArray( aValues )    
      
   REDEFINE GET oGetCodArt VAR cCod ID 100 OF oDlg_GenAut PICTURE '@!S12' UPDATE;
            VALID ( MsgInfo( "Ejecutando Valid" ), .t. )

   REDEFINE GET oGetUdsPed VAR nCan ID 140 OF oDlg_GenAut PICTURE '@EZ 999,999' UPDATE;
            VALID ( oGetCodArt:Disable(), oGetUdsPed:Disable(), oBrowPed:Refresh(), .t. )

   ACTIVATE DIALOG oDlg_GenAut CENTERED ON INIT ( oGetCodArt:Disable(), oGetUdsPed:Disable() )

   DbCloseAll()

return nil
Fichero RC:

Code: Select all

#include <windows.h>
#include <commctrl.h>
#include <richedit.h>

LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_US

DLG_GENAUT DIALOG DISCARDABLE 65, 47, 231, 193
STYLE WS_POPUP|DS_MODALFRAME|WS_CAPTION|WS_SYSMENU|WS_VISIBLE
CAPTION "Prueba"
FONT 8, "MS Sans Serif"
{
  CONTROL "", 90, "TWBrowse", WS_DISABLED|WS_TABSTOP|0x00b00000, 4, 4, 224, 149
  CONTROL "Código:", -1, "Static", WS_GROUP, 4, 164, 28, 9
  CONTROL "", 100, "Edit", WS_BORDER|WS_TABSTOP, 36, 160, 63, 12
  CONTROL "Cantidad:", -1, "Static", WS_GROUP, 4, 180, 32, 8
  CONTROL "", 140, "Edit", WS_BORDER|WS_TABSTOP, 36, 176, 38, 12
  CONTROL "Añadir", 4050, "Button", WS_TABSTOP, 120, 160, 52, 12
}

 

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 7:43 am
by hmpaquito
Triumvirato,

Algunas veces tuve problemas con el SetFocus(). Para ello hice esta funcion ForceFocus() para que sea "casi imposible" que un objeto NO reciba el foco cuando se le "pasa".

Saludos

Code: Select all

//////////////////////////////////////////////////////////////////
FUNCTION ForceFocus(oJump)
Local oFocus:= oWndFromHwnd(GetFocus())  // Indocumentada (Window.Prg)
Local bValid, bWhen
*
IF If(oFocus != NIL, oFocus:hWnd != oJump:hWnd, .t.)  // No sea el mismo objeto el
                                                      // actual y el que se salta
   *
   IF oFocus != NIL
      bWhen:=         oFocus:bWhen
      bValid:=        oFocus:bValid
      oFocus:bWhen:=  {|| .f. }
      oFocus:bValid:= {|| .t.}  // Este no se si necesario, pero por si acaso.
   ENDIF
   *
   *
   IF __ObjHasMethod(oJump, "FORWHEN")  // en _ oJump es de la clase TWindow
      oJump:ForWhen()
   ENDIF
   *
   *
   oJump:SetFocus()
   *
   IF oFocus != NIL
      IF !(oFocus:ClassName() $ "TWINDOW,TDIALOG")
         IF bWhen == NIL
            // Necesarisimo, pq si es NIL, entonces en el ForWhen()
            // no se entera pq en window en aEval() se lo salta
            // si bWhen es NIL y entonces no lo Enable()
            oFocus:bWhen:=  {|| .t. }
            *
         ELSE
            oFocus:bWhen := bWhen
         ENDIF

         // Solo si deriva de TControl
         oFocus:ForWhen()
      ENDIF
      *
      oFocus:bWhen := bWhen
      oFocus:bValid:= bValid
   ENDIF

   *
ENDIF
*
RETURN NIL
*
 

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 8:15 am
by triumvirato
hmpaquito,

he sustituido la línea donde llamo al Setfocus() por esta otra:

Code: Select all

   REDEFINE BUTTON oBtnAnade ID 4050 OF oDlg_GenAut UPDATE ACTION ( oGetCodArt:Enable(), oGetUdsPed:Enable(), ForceFocus( oGetCodArt ) )
 
Pero no funciona. No sé si es correcta su sintaxis.

Muchas gracias.

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 10:24 am
by hmpaquito
triumvirato,

En general no me gusta mucho la idea de ir habilitando cosas con el obj:Enable(). Lo que suelo hacer yo es especificar la condicion en la clausula WHEN. Al menos y a mi modo de ver el codigo es mas legible y mantenible.
Para ver qué está pasando pondría un msginfo("hola") a ver si al pulsar intro sobre el botón el mensaje aparece.
No se si con la funcion ForceFocus(), dentro, o en soledad, pon oBtnAnade:oJump:= oGetCodArt.

A ver si te vale algo de esto.

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 10:57 am
by triumvirato
hmpaquito wrote: En general no me gusta mucho la idea de ir habilitando cosas con el obj:Enable(). Lo que suelo hacer yo es especificar la condicion en la clausula WHEN. Al menos y a mi modo de ver el codigo es mas legible y mantenible.
Correcto, tampoco soy partidario, pero busco un comportamiento un tanto especial que no soy capaz de hacer con la cláusula WHEN.
hmpaquito wrote: Para ver qué está pasando pondría un msginfo("hola") a ver si al pulsar intro sobre el botón el mensaje aparece.
Todas las funtiones, etc. que pongo en la cláusula ACTION del botón se cumplen incluido un MsgInfo() si lo colocamos, excepto el SetFocus() si se hace con la tecla Enter. Presionando el boton con el raton lo hace perfecto. Es sólamente si se pulsa el Enter.
hmpaquito wrote: No se si con la funcion ForceFocus(), dentro, o en soledad, pon oBtnAnade:oJump:= oGetCodArt.
Tampoco me ha funcionado en las formas que comentas.

Muchas gracias... a ver si alguien arroja un poco más de luz, porque me parece bastante extraño que con el raton funcione siempre bien y pulsando el enter tenga un comportamiento distinto.

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 11:50 am
by hmpaquito
Se me ocurre:

1º SysRefresh()

... ACTION (oBtn:oJump:= oGetArt, SysRefresh() )

2º PostMsg()

... ACTION (oGetArt:PostMsg(WM_SETFOCUS))

A ver si alguno de estos funciona.

PD. Por todas estas cosas es por las que tengo todos los COMANDOS de Fwh redefinidos, para abstraer todos estos problemas.

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 2:46 pm
by triumvirato
hmpaquito,

Gracias por tu tiempo, de verdad. A ver si damos con ello.
hmpaquito wrote:1º SysRefresh()
... ACTION (oBtn:oJump:= oGetArt, SysRefresh() )
No funciona. Es curioso, con esta forma tiene la misma reacción si pulsas enter, como si lo haces con el raton, fallan las dos. Y ya de paso he probado más combinaciones con sysrefresh() sin éxito.
hmpaquito wrote:2º PostMsg()
... ACTION (oGetArt:PostMsg(WM_SETFOCUS))
Esta segunda forma parece que va mas encaminada. El problema está en que una vez ejecutada (con raton o teclado), no puedo escribir en el get que ha tomado el foco, no aparece tampoco el cursor, pero si respeta el tema del foco.

Cómo es eso que dices que tienes redefinidos todos los comandos de FWH para evitar estos problemas?.

Muchas gracias por tu tiempo. Te estoy muy agradecido.

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 3:37 pm
by hmpaquito
Cómo es eso que dices que tienes redefinidos todos los comandos de FWH para evitar estos problemas?.
Un ejemplo al vuelo:

Code: Select all

#Translate @ <nRow>, <nCol> MIGET <oGet> ;
                  [PREVALID <bPreValid>] ;                         // Action PreValid
                  [VALID <bValid> ;                                    // Exclusivamente condicion de validacion: luego es facil preguntar solo por las condiciones de validacion de todos los gets
                 [ POSTVALID <bPostValid>]                        // Action PostValid
                 [JUMPTO <oJump> ];
                 => ;
                @ <nRow>, <nCol> GET <oGet> ;
                       VALID (;
                                   If(<.bPreValid.>, Eval(<bPreValid>), NIL),;
                                   l:= Eval(<bValid>),;
                                   If(<.bPostValid.>, Eval(<bPostValid>), NIL),;
                                   If(l .AND. <.oJump.>, ForceFocus(<oJump>), NIL);
                                  )
 
Con esto se consigue:
1º Aislar los malfuncionamientos o funcionamientos incomodos. Por ejemplo utilizando la clausula JUMPTO para saltar a otro control. Si en futuras modificaciones de FWH no funcionara pues se cambia en un solo punto y ya esta.
2º Aislar el VALID para lo que realmente es: Evaluar una expresion que devuelva .f. o .t.. El prevalid o postvalid sirven para otra cosa.
3º Poder cambiar desde el comando diversos aspectos de funcionamiento sin tocar los comandos o fuentes originales de fwh.

Es solo una idea que nada tiene que ver con el tema que llevamos entre manos

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 4:01 pm
by triumvirato
hmpaquito,

Ahí ya si me pierdo del todo. Más o menos he entendido para qué lo usas, pero mis conocimientos no dan para tanto, aunque veo ya cómo usas tu función ForceFocus. Efectivamente cualquier cambio o problema es más sencillo de solucionar.

Lo de mi problema a ver si hay alguien que arroje más luz al tema porque llevo dos dias probando casi de todo (para lo que mis conocimientos dan).

Gracias, un saludo.

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 5:05 pm
by Daniel Garcia-Gil
triumvirato...

habria que cambiar la clase para hacer una mejor simulacion del click... actualmente se envia el mensaje FM_CLICK pero habria que hacerle un mejor "engaño"

abre el archivo button.prg

y cambia la linea

Code: Select all

              ::PostMsg( FM_CLICK )    // fire the button's action
 
por esto

Code: Select all

            ::PostMsg( WM_LBUTTONDOWN, 1, 1 )
            ::PostMsg( WM_LBUTTONUP, 1, 1 )
 
por tratarse de un dialogo debes asignarle DLGC_WANTALLKEYS a la data nDlgCode, sino esas pulsaciones no llegaran al control

Code: Select all

oBtnAnade:nDlgCode = DLGC_WANTALLKEYS
todo lo demas queda igual

te dejo el ejecutable...

http://www.sitasoft.net/fivewin/samples/testtri.zip

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 6:04 pm
by triumvirato
Daniel,

Muchas gracias por tu ayuda.

He recompilado con _ que me indicas, pero sigue ocurriendo el mismo comportamiento, con el raton hace el SetFocus(), con la tecla enter encima del boton lo ignora.
Uso FWH/FWHX 10.1 26/January/2010
Por otro lado, efectivamente, el exe que me envías lo hace correctamente.

¿Esa modificación que me indicas en button.prg quedará en próximas versiones de FiveWin, o por el contrario debo hacer la modificación (en caso de que consiga que funcione) en las actualizaciones sucesivas de Fivewin?

Muchas gracias, Saludos.

Re: Problema con Button y Setfocus()

Posted: Thu Aug 19, 2010 6:27 pm
by andresreyes_mzt
Triumvirato,

Desgraciadamente Windows maneja de forma muy diferente Ventanas Normales y Dialogos, en la mayoria de las ocasiones la llamada a la Funciona SetFocus no Funciona dentro de los dialogos.

La forma que establece windows para cambiar el foco dentro de dialogos es por medio del mensaje WM_NEXTDLGCTL

Incluye esto al principio de tu PRG

Code: Select all

#define WM_NEXTDLGCTL                   0x0028
 
Esta Linea la Eliminas

Code: Select all

REDEFINE BUTTON oBtnAnade ID 4050 OF oDlg_GenAut UPDATE ACTION ( oGetCodArt:Enable(), oGetUdsPed:Enable(), oGetCodArt:SetFocus() )
 
Y la Cambias por esta ...

Code: Select all

REDEFINE BUTTON oBtnAnade ID 4050 OF oDlg_GenAut UPDATE ACTION ( oGetCodArt:Enable(), oGetUdsPed:Enable(), oDlg_GenAut:PostMsg( WM_NEXTDLGCTL, oGetCodArt:hWnd, 1 ) )
 
The WM_NEXTDLGCTL message is sent to a dialog box procedure to set the keyboard focus to a different control in the dialog box.

WM_NEXTDLGCTL
wCtlFocus = wParam; // identifies control for focus
fHandle = (BOOL) LOWORD(lParam); // wParam handle flag


Parameters

wCtlFocus

Value of wParam. If the fHandle parameter is TRUE, the wCtlFocus parameter identifies the control that receives the focus. If fHandle is FALSE, wCtlFocus is a flag that indicates whether the next or previous control with the WS_TABSTOP style receives the focus. If wCtlFocus is zero, the next control receives the focus; otherwise, the previous control with the WS_TABSTOP style receives the focus.

fHandle

Value of lParam. Contains a flag that indicates how Windows uses the wCtlFocus parameter. If the fHandle parameter is TRUE, wCtlFocus is a handle associated with the control that receives the focus; otherwise, wCtlFocus is a flag that indicates whether the next or previous control with the WS_TABSTOP style receives the focus.



Return Values

An application should return zero if it processes this message.

Remarks

The effect of this message differs from that of the SetFocus function because WM_NEXTDLGCTL modifies the border around the control.
Do not use the SendMessage function to send a WM_NEXTDLGCTL message if your application will concurrently process other messages that set the focus. Use the PostMessage function instead.
Es solo un cambio pequeño lo que necesitas hacer y con eso solucionas tu problema ... sin tanto codigo ...

Saludos,
Andres Reyes

Re: Problema con Button y Setfocus()

Posted: Fri Aug 20, 2010 6:09 am
by triumvirato
Andrés,

Simplemente, perfecto.

Se me escapa bastante todo este tipo de soluciones, sinceramente, por lo que quedo maravillado del nivel de muchos de los que por aquí ayudáis.

Mcuhas gracias, Saludos.

Re: Problema con Button y Setfocus()

Posted: Sun Aug 07, 2011 11:46 pm
by carlos vargas
me has salvado, sos grande, aca mi codigo personalizado

Code: Select all

...
EXTEND CLASS TDIALOG    WITH METHOD MySetFocus
...

FUNCTION MySetFocus( xData )
   LOCAL Self := HB_QSelf()

   DO CASE
   CASE ValType( xData )=="O"
      ::PostMsg( WM_NEXTDLGCTL, xData:hWnd, 1 )
   CASE ValType( xData )=="N"
      IF Len( ::aControls ) >= xData
         ::PostMsg( WM_NEXTDLGCTL, ::aControls[ xData ]:hWnd, 1 )
      ENDIF
   ENDCASE

RETURN NIL
...
 
y aca como lo uso

Code: Select all

PROCEDURE FlujoRuta_SeleccionarRuta()
   LOCAL oDBSearch := TDBSearch():new( "Lista de ruta de cobro" )

   WITH OBJECT oDBSearch
      :cAlias   := "RUTA"
      :bFields  := { || { RUTA->NUM_RUTA, RUTA->NOMBRE } }
      :aHeaders := { "Número", "Nombre de gestor" }
      :aJustify := { 2, 0 }
      :aOrders  := { { "NUM_RUTA" }, { "Numero de ruta" } }
      :cCurOrd  := "NUM_RUTA"
      :nCurOrd  := 1
      IF :open()
         cNumRuta := RUTA->NUM_RUTA
         cNomRuta := RUTA->NOMBRE
         cNomCobr := COBR->NOMBRE
         oDlgE:update()
         oDlgE:MySetFocus( 9 )  //aca puedo usar la posicion del objeto en aControls
         //oDlgE:MySetFocus( oGet )   //aca puedo usar un objeto
      ENDIF
   END

RETURN
 
salu2
carlos vargas