Check value in Dialogs EditBox

Started by jmvmla, April 12, 2018, 02:11:23 AM

Previous topic - Next topic

jmvmla

Hi all,

I'm having some EditBox and a PushButton in a DialogBox
The PushButton is used to go ahead in the script and to validate the value entered in every EditBox
So, this validation is always done after all the EditBoxes have been filled

Now, how is it possible to check the complete value (not Char by Char) in every EditBox while leaving it and before entering value in the next EditBox ?

Thanks for your infos

jm

stanl

You might want to look at
http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/tsleft.web+WinBatch/Dialog~Editor/Dialog~Editor~version~6.X/Samples+Field~validation~with~WinBatch~Dialog.txt

or

http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/tsleft.web+WinBatch/Dialog~Editor/Dialog~Editor~version~6.X/Samples+Field~Validation~Example.txt

but in any case you will probably require a dynamic dialog with a callback.

[EDIT]: and also, you can consider using validating drop-downs rather than regular edits, for example a field that says "Select State" - then there is always the 'wizard' concept where you step through a series of validations.

kdmoyers

I've always wished there was an event for focus loss, so that your code could run when the user moves the cursor away from the field.  Here's the hack I've been using for the last several years:

Code (winbatch) Select
;WasSimpleFocusLossEvent.wbt
;
; Sometimes new people are suprised to find that the EditBox
; event (number 5) fires for every keystroke -- what they
; really wanted to know is when the user is DONE changing
; the EditBox.  This can be detected by watching when the
; focus moves away from the control - the "focus lost" event.
; Then we can look at the data and see if any action is needed.
;
; Also demoed here is a simple and handy StatusBar function.
;
;

#DefineFunction DProc(DH,DM,DN,DE,DC)
  ; Demonstration of WasFocusLossEvent and StatusBar

  ; detect the focus loss pseudo-event
  if WasFocusLossEvent(DH,&Controlname)
    ; only if the control name starts with "EditBox"
    if strindex(controlname,"EditBox",1,0)
      ; what did it contain?
      t = dialogcontrolget(DH, controlname, 3)

      ;; here is where you do act upon the event, checking field
      ;; contents (because you know the field name) and possibly
      ;; steering the focus back onto the field, or whatever

      ; in this case, just put all that in the status bar
      StatusBar(2, controlname:" lost focus, contains ":t)
    endif
  endif

  ; normal event processing Switch
  switch DM       
    case 0 ; initialization event
      DialogProcOptions(DH,1,200)    ; timer events every 200 ms
      DialogProcOptions(DH,2,@TRUE)  ; pushbutton events
      StatusBar(DH, "75,")           ; set up a two-section statusbar
      return(-1)
    case 1 ; timer event
      ; display the time in the status bar
      StatusBar(1, strsub(timeymdhms(),12,8))
      return(-1)
    case 2 ; pushbutton
      message(DN,'exiting')
      return(-1)
  endswitch
  return(-1)
#EndFunction             



#definefunction WasFocusLossEvent(DiaHan,ptrControlname)
  ; Detects a focus loss pseudo event
  ;
  ; if function returns @TRUE, then Controlname will be
  ;   set to the control that just lost focus.  Controlname
  ;   must be passed as a pointer (using the ampersand).
  ;
  ; Be sure dialog has a timer event of 500ms or less.
  ; Add to dialog callback BEFORE normal event handling.

  ; set up to remember control
  ptrLastFocus = PtrPersistent(LastFocus,"")
  ; see what control currently has focus
  DCSTATE_GETFOCUS = 5
  ThisFocus = DialogControlState(DiaHan,0,DCSTATE_GETFOCUS,0)
  ; same control?
  if ThisFocus == *ptrLastFocus
    ; return nothing, no focus change
    return @false
  else
    ; no previous known control?
    if *ptrLastFocus == ""
      *ptrLastFocus = ThisFocus ; reset for this new control
      return @false
    endif
    terminate(isdefined(*ptrControlname)==-1,'WasFocusLossEvent','Controlname must be pointer')
    *ptrControlname = *ptrLastFocus  ; control that lost focus
    *ptrLastFocus = ThisFocus        ; reset for this new control
    return @true
  endif
#endfunction
;
; Note: the WasFocusLossEvent function requires a timer
; event because it is essentially polling to discover the
; change in focus.  It is not necessary to do anything
; with the timer event if you do not want to.  Fire the
; event every half second or less to get a snappy response
; to focus changes. 
;
; Note: the WasFocusLossEvent function should be called
; ahead of the normal dialog event processing in order to
; detect and process the focus change before processing the
; event that changed it.  For example, if clicking a Save
; button changes the focus, you want to first deal with the
; focus change, then deal with the Save operation.


#definefunction StatusBar(N,text)
    ; Super easy status bar on winbatch dialogs
    ; (repackaged from article W15451 in the database)
    ;
    ; on first call,       N is the Dialog Handle and
    ;                      text is "width,width,width..."
    ;                      which establishes the number of sections
    ;                      and gives the width in pixels for each section
    ; on subsequent calls, N is the section number (1 based) and
    ;                      text is content for that section
    ;
    user32=strcat(dirwindows(1),"user32.dll")
    WM_USER=1024
    SB_SETPARTS=WM_USER+4
    SB_SETTEXT=WM_USER+1
    h = ptrpersistent(handle,0) ; remember the status bar handle between calls
    if *h == 0 ; create mode?
      hinst=dllhinst("")
      style = 0
      *h = dllcall(user32,long:"CreateWindowExA",long:512,lpstr:"msctls_statusbar32",lpstr:"",long:1073741824|268435456|style,long:0,long:0,long:0,long:0,long:N,long:0,long:hinst,long:0)
      n = itemcount(text,',')   
      awidths=binaryalloc(n*4)
      t = 0
      for i = 0 to n-2
        x = itemextract(i+1,text,',')
        if !isnumber(x) then x = 0
        binarypoke4(awidths,i*4,t+x)
        t = t + x
      next i
      binarypoke4(awidths,i*4,-1)
      dllcall(user32, long:"SendMessageA", long:*h, long:SB_SETPARTS, long:n, lpbinary:awidths)
    else
      dllcall(user32,long:"SendMessageA",long:*h,long:SB_SETTEXT,long:N-1,lpstr:text)
    endif
#endfunction

MyDialogFormat=`WWWDLGED,6.2`

MyDialogCaption=`Testing FocusLoss Event`
MyDialogX=167
MyDialogY=052
MyDialogWidth=146
MyDialogHeight=127
MyDialogNumControls=007
MyDialogProcedure=`DProc`
MyDialogFont=`DEFAULT`
MyDialogTextColor=`DEFAULT`
MyDialogBackground=`DEFAULT,DEFAULT`
MyDialogConfig=0

MyDialog001=`051,081,036,012,PUSHBUTTON,"PushButton_OK",DEFAULT,"OK",1,10,@csDefButton,DEFAULT,DEFAULT,DEFAULT`
MyDialog002=`011,017,036,012,EDITBOX,"EditBox_1",ebVariable1,"A",DEFAULT,1,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog003=`075,017,036,012,EDITBOX,"EditBox_2",ebVariable2,"BB",DEFAULT,2,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog004=`013,051,036,012,EDITBOX,"EditBox_3",ebVariable3,"CCC",DEFAULT,3,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog005=`077,049,036,012,EDITBOX,"EditBox_4",ebVariable4,"DDDD",DEFAULT,4,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog006=`003,105,138,012,STATICTEXT,"StaticText_1",DEFAULT,"current time                      focus loss info",DEFAULT,60,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog007=`001,001,138,006,STATICTEXT,"StaticText_2",DEFAULT,"Use Tab key or mouse to move between fields",DEFAULT,70,DEFAULT,DEFAULT,DEFAULT,DEFAULT`

ButtonPushed=Dialog("MyDialog")

exit
The mind is everything; What you think, you become.

td

In the case of multiple edit boxes, it is not absolutely necessary to track the input focus to validate boxes one at a time.  All that's needed is to use the edit box's "@deEdText"  event to note which box is currently being filled.  Then when the even switches to the next edit box, validate the previous edit box and put the input focus back on the previous box if the entry was not correct using the "DialogControlState" function's "@dcsSetFocus" request code.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

kdmoyers

That's true, but you still end up looking at the box many times, at each keystroke, which can get expensive if what you are doing is a database lookup or something.

Here's another version of the same thing, but coded in a way that makes it more like a real event.

Code (winbatch) Select
;WasFullFocusLossEvent.wbt
;
; This sample shows the same idea, only treating the pseudo event more
; like a real event, which can appear more confusing, but is actually
; simpler to code in practice.
;
;

#DefineFunction DProc(DH,DM,DN,DE,DC)
  ; Demonstration of WasFocusLossEvent and StatusBar

  ; detect the focus loss pseudo-event
  if WasFocusLossEvent(DH,&DN)
    DM = 99 ; my pseudo focus loss event code
  endif

  ; normal event processing Switch
  switch 1       
    case DM==@deInit    ; initialization event
      DialogProcOptions(DH,1,200)    ; timer events every 200 ms
      DialogProcOptions(DH,2,@TRUE)  ; pushbutton events
      StatusBar(DH, "75,")           ; set up a two-section statusbar
      return(-1)
    case DM==@deTimer   ; timer event
      ; display the time in the status bar
      StatusBar(1, strsub(timeymdhms(),12,8))
      return(-1)
    case DM==@dePbPush  ; pushbutton
      message(DN,'exiting now')
      return(-1)
    case DM==99 && DN=="EditBox_1"   ;first box lost focus
      t = dialogcontrolget(DH, DN, 3) ;
      StatusBar(2, "first box contains ":t)
      return(-1)
    case DM==99 && DN=="EditBox_2"   ;second box lost focus
      t = dialogcontrolget(DH, DN, 3) ;
      StatusBar(2, "second box contains ":t)
      return(-1)
    case DM==99 && DN=="EditBox_3"   ;third box lost focus
      t = dialogcontrolget(DH, DN, 3) ;
      StatusBar(2, "third box contains ":t)
      return(-1)
    case DM==99 && DN=="EditBox_4"   ;fourth box lost focus
      t = dialogcontrolget(DH, DN, 3) ;
      StatusBar(2, "fourth box contains ":t)
      return(-1)
  endswitch
  return(-1)
#EndFunction             



#definefunction WasFocusLossEvent(DiaHan,ptrControlname)
  ; Detects a focus loss pseudo event
  ;
  ; if function returns @TRUE, then Controlname will be
  ;   set to the control that just lost focus.  Controlname
  ;   must be passed as a pointer (using the ampersand).
  ;
  ; Be sure dialog has a timer event of 500ms or less.
  ; Add to dialog callback BEFORE normal event handling.

  ; set up to remember control
  ptrLastFocus = PtrPersistent(LastFocus,"")
  ; see what control currently has focus
  DCSTATE_GETFOCUS = 5
  ThisFocus = DialogControlState(DiaHan,0,DCSTATE_GETFOCUS,0)
  ; same control?
  if ThisFocus == *ptrLastFocus
    ; return nothing, no focus change
    return @false
  else
    ; no previous known control?
    if *ptrLastFocus == ""
      *ptrLastFocus = ThisFocus ; reset for this new control
      return @false
    endif
    terminate(isdefined(*ptrControlname)==-1,'WasFocusLossEvent','Controlname must be pointer')
    *ptrControlname = *ptrLastFocus  ; control that lost focus
    *ptrLastFocus = ThisFocus        ; reset for this new control
    return @true
  endif
#endfunction
;
; Note: the WasFocusLossEvent function requires a timer
; event because it is essentially polling to discover the
; change in focus.  It is not necessary to do anything
; with the timer event if you do not want to.  Fire the
; event every half second or less to get a snappy response
; to focus changes. 
;
; Note: the WasFocusLossEvent function should be called
; ahead of the normal dialog event processing in order to
; detect and process the focus change before processing the
; event that changed it.  For example, if clicking a Save
; button changes the focus, you want to first deal with the
; focus change, then deal with the Save operation.


#definefunction StatusBar(N,text)
    ; Super easy status bar on winbatch dialogs
    ; (repackaged from article W15451 in the database)
    ;
    ; on first call,       N is the Dialog Handle and
    ;                      text is "width,width,width..."
    ;                      which establishes the number of sections
    ;                      and gives the width in pixels for each section
    ; on subsequent calls, N is the section number (1 based) and
    ;                      text is content for that section
    ;
    user32=strcat(dirwindows(1),"user32.dll")
    WM_USER=1024
    SB_SETPARTS=WM_USER+4
    SB_SETTEXT=WM_USER+1
    h = ptrpersistent(handle,0) ; remember the status bar handle between calls
    if *h == 0 ; create mode?
      hinst=dllhinst("")
      style = 0
      *h = dllcall(user32,long:"CreateWindowExA",long:512,lpstr:"msctls_statusbar32",lpstr:"",long:1073741824|268435456|style,long:0,long:0,long:0,long:0,long:N,long:0,long:hinst,long:0)
      n = itemcount(text,',')   
      awidths=binaryalloc(n*4)
      t = 0
      for i = 0 to n-2
        x = itemextract(i+1,text,',')
        if !isnumber(x) then x = 0
        binarypoke4(awidths,i*4,t+x)
        t = t + x
      next i
      binarypoke4(awidths,i*4,-1)
      dllcall(user32, long:"SendMessageA", long:*h, long:SB_SETPARTS, long:n, lpbinary:awidths)
    else
      dllcall(user32,long:"SendMessageA",long:*h,long:SB_SETTEXT,long:N-1,lpstr:text)
    endif
#endfunction

MyDialogFormat=`WWWDLGED,6.2`

MyDialogCaption=`Testing FocusLoss Event`
MyDialogX=167
MyDialogY=052
MyDialogWidth=146
MyDialogHeight=127
MyDialogNumControls=007
MyDialogProcedure=`DProc`
MyDialogFont=`DEFAULT`
MyDialogTextColor=`DEFAULT`
MyDialogBackground=`DEFAULT,DEFAULT`
MyDialogConfig=0

MyDialog001=`051,081,036,012,PUSHBUTTON,"PushButton_OK",DEFAULT,"OK",1,10,@csDefButton,DEFAULT,DEFAULT,DEFAULT`
MyDialog002=`011,017,036,012,EDITBOX,"EditBox_1",ebVariable1,"A",DEFAULT,1,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog003=`075,017,036,012,EDITBOX,"EditBox_2",ebVariable2,"BB",DEFAULT,2,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog004=`013,051,036,012,EDITBOX,"EditBox_3",ebVariable3,"CCC",DEFAULT,3,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog005=`077,049,036,012,EDITBOX,"EditBox_4",ebVariable4,"DDDD",DEFAULT,4,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog006=`003,105,138,012,STATICTEXT,"StaticText_1",DEFAULT,"current time                      focus loss info",DEFAULT,60,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog007=`001,001,138,006,STATICTEXT,"StaticText_2",DEFAULT,"Use Tab key or mouse to move between fields",DEFAULT,70,DEFAULT,DEFAULT,DEFAULT,DEFAULT`

ButtonPushed=Dialog("MyDialog")

exit
The mind is everything; What you think, you become.

kdmoyers

If you need to process editboxes BEFORE pushbuttons, then the events loop can be rearranged in various ways.
The mind is everything; What you think, you become.

JTaylor

Not necessarily.   Chances are the event is running anyway.  You don't have to check the values of the entries for what Tony suggests (assuming I understand).  You just maintain two variables.

current_focus and previous_focus 

When current no longer matches previous then you know you need to validate the previous_focus control.

Jim



Quote from: kdmoyers on April 12, 2018, 07:53:24 AM
That's true, but you still end up looking at the box many times, at each keystroke, which can get expensive if what you are doing is a database lookup or something.



kdmoyers

<< You just maintain two variables.  current_focus and previous_focus  When current no longer matches previous then you know you need to validate the previous_focus control >>

Which is precisely what the code I posted does.  It just does it in a way that lets your program treat it like an ordinary event.
The mind is everything; What you think, you become.

JTaylor

Okay.  Didn't read your code closely.  Just responded to what you said.   :)

Jim

td

Quote from: kdmoyers on April 12, 2018, 07:53:24 AM
That's true, but you still end up looking at the box many times, at each keystroke, which can get expensive if what you are doing is a database lookup or something.


The assertion that checking which edit box is producing a text input event is any more expensive than kicking off every two seconds to check which control has the focus seems a bit questionable    In fact, it could even be less expensive because the UDC would be called less often.

However, either way will work.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

kdmoyers

Thanks -- good points Tony and Jim.  You guys always make me think. I do like the timer-based thing though.
By unwrapping the function call, and switching to DefineSubroutine, I get a shorter and clearer version below.
I'll have to think about a no-timer version.
Code (winbatch) Select
;WasOtherFocusLossEvent.wbt
;
LostFocus = 0

#DefineSubroutine DProc(DH,DM,DN,DE,DC)
  ; Demonstration of WasFocusLossEvent and StatusBar

  ; detect the focus loss pseudo-event
  DCSTATE_GETFOCUS = 5
  ThisFocus = DialogControlState(DH,0,DCSTATE_GETFOCUS,0)
  if ThisFocus != LostFocus
    switch 1       
      case LostFocus=="EditBox_1"   ;first box lost focus
        t = dialogcontrolget(DH, LostFocus, 3) ;
        StatusBar(2, "first box contains ":t)
        if strupper(t)==t then break ; is good
        StatusBar(2, "ERROR: must be upper case")
        DialogControlState(DH,LostFocus,@dcsSetFocus,0)
        return(-2) ; don't process this event call further
      case LostFocus=="EditBox_2"   ;second box lost focus
        t = dialogcontrolget(DH, LostFocus, 3) ;
        StatusBar(2, "second box contains ":t)
        if strlower(t)==t then break ; is good
        StatusBar(2, "ERROR must be lower case")
        DialogControlState(DH,LostFocus,@dcsSetFocus,0)
        return(-2) ; don't process this event call further
      case LostFocus=="EditBox_3"   ;third box lost focus
        t = dialogcontrolget(DH, LostFocus, 3) ;
        StatusBar(2, "third box contains ":t)
        break
      case LostFocus=="EditBox_4"   ;fourth box lost focus
        t = dialogcontrolget(DH, LostFocus, 3) ;
        StatusBar(2, "fourth box contains ":t)
        break
    endswitch
    LostFocus = ThisFocus
  endif

  ; normal event processing Switch
  switch 1       
    case DM==@deInit    ; initialization event
      DialogControlState(DH,"EditBox_1",@dcsSetFocus,0)
      DialogProcOptions(DH,1,200)    ; timer events every 200 ms
      DialogProcOptions(DH,2,@TRUE)  ; pushbutton events
      StatusBar(DH, "75,")           ; set up a two-section statusbar
      return(-2)
    case DM==@deTimer   ; timer event
      ; display the time in the status bar
      StatusBar(1, strsub(timeymdhms(),12,8))
      return(-2)
    case DM==@dePbPush  ; pushbutton
      display(1,DN,'exiting now')
      return(1)
  endswitch
  return(-2)
#EndSubroutine           


#definefunction StatusBar(N,text)
    ; Super easy status bar on winbatch dialogs
    ; (repackaged from article W15451 in the database)
    ;
    ; on first call,       N is the Dialog Handle and
    ;                      text is "width,width,width..."
    ;                      which establishes the number of sections
    ;                      and gives the width in pixels for each section
    ; on subsequent calls, N is the section number (1 based) and
    ;                      text is content for that section
    ;
    user32=strcat(dirwindows(1),"user32.dll")
    WM_USER=1024
    SB_SETPARTS=WM_USER+4
    SB_SETTEXT=WM_USER+1
    h = ptrpersistent(handle,0) ; remember the status bar handle between calls
    if *h == 0 ; create mode?
      hinst=dllhinst("")
      style = 0
      *h = dllcall(user32,long:"CreateWindowExA",long:512,lpstr:"msctls_statusbar32",lpstr:"",long:1073741824|268435456|style,long:0,long:0,long:0,long:0,long:N,long:0,long:hinst,long:0)
      n = itemcount(text,',')   
      awidths=binaryalloc(n*4)
      t = 0
      for i = 0 to n-2
        x = itemextract(i+1,text,',')
        if !isnumber(x) then x = 0
        binarypoke4(awidths,i*4,t+x)
        t = t + x
      next i
      binarypoke4(awidths,i*4,-1)
      dllcall(user32, long:"SendMessageA", long:*h, long:SB_SETPARTS, long:n, lpbinary:awidths)
    else
      dllcall(user32,long:"SendMessageA",long:*h,long:SB_SETTEXT,long:N-1,lpstr:text)
    endif
#endfunction

MyDialogFormat=`WWWDLGED,6.2`

MyDialogCaption=`Testing FocusLoss Event`
MyDialogX=167
MyDialogY=052
MyDialogWidth=146
MyDialogHeight=127
MyDialogNumControls=007
MyDialogProcedure=`DProc`
MyDialogFont=`DEFAULT`
MyDialogTextColor=`DEFAULT`
MyDialogBackground=`DEFAULT,DEFAULT`
MyDialogConfig=0

MyDialog001=`051,081,036,012,PUSHBUTTON,"PushButton_OK",DEFAULT,"OK",1,10,@csDefButton,DEFAULT,DEFAULT,DEFAULT`
MyDialog002=`011,017,036,012,EDITBOX,"EditBox_1",ebVariable1,"A",DEFAULT,1,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog003=`075,017,036,012,EDITBOX,"EditBox_2",ebVariable2,"BB",DEFAULT,2,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog004=`013,051,036,012,EDITBOX,"EditBox_3",ebVariable3,"CCC",DEFAULT,3,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog005=`077,049,036,012,EDITBOX,"EditBox_4",ebVariable4,"DDDD",DEFAULT,4,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog006=`003,105,138,012,STATICTEXT,"StaticText_1",DEFAULT,"current time                      focus loss info",DEFAULT,60,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MyDialog007=`001,001,138,006,STATICTEXT,"StaticText_2",DEFAULT,"Use Tab key or mouse to move between fields",DEFAULT,70,DEFAULT,DEFAULT,DEFAULT,DEFAULT`

ButtonPushed=Dialog("MyDialog")

exit
The mind is everything; What you think, you become.

kdmoyers

<< I'll have to think about a no-timer version >>
Duh -- just ask for the @deEdText event instead of a timer event.  That was simple.
Thanks guys!
The mind is everything; What you think, you become.

td

For the sake of accuracy, I typed "every 2 seconds" above when I should have typed "every one-half second."
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

jmvmla

With the last code of KdMoyers, it's exactly what I was looking for
Now it's time to study it :)
Thanks a lot for your advices
jm

stanl

All this still begs the question as to what is the goal.  Is the dialog merely a form to fill out, or a process, i.e. an update/install. The latter would fit a wizard approach, the former more of a form validation. Then the former can be broken down as :  validate-field is null, validate field has to be filled in [upper/lower case, or lookup table], validate field filed with mask, i.e. SSAN, Phone #.....

I'm sure all contributors have experienced the above.  But, just curious