Announcement

Collapse
No announcement yet.

Safe to validate. When?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Safe to validate. When?

    When should be validation on a field by field basis be done. What event? Killfocus won't work if you need to branch to set focus to a field based on the contents of the field you are validating.

    Do you set a global variable with the id and what to do info in the Killfocus event if there is an error and call a function in the set focus of every control on the screen to redirect to a catchall traffic cop function that will either go where the keyboard or mouse directs or force the cursor back to the field where an error occurred.

    In some cases the en_change event will work fine but what if the value can't be evaluated until the user attempts to leave the field?

    Bob Mechler

  • #2
    KillFocus has been working fine for me.
    Moving to another field based on the contents
    of the field just validated works fine. The only problem I had was the
    need to take into account that killfocus is fired when the app loses focus
    and the user might not be done with the field. Not sure if it matters but
    all my gui work is done in FireFly.
    Last edited by Paul D. Elliott; 22 Apr 2009, 06:23 AM.

    Comment


    • #3
      For edit box, WM_COMMAND/EN_CHANGE is good. (or EN_UPDATE). Of course, those are character by character.

      The thing about trying to do field-by-field validation, the user has more than one way to say, "I'm done with this field": Tab to next field, click on another field, click on a button all come to mind. For the most part WM_KILLFOCUS handles all these.

      For the most part when the user has to enter a valid value in an edit control, I do character-by-character, and depending on the current value of the control I enable or disable any "action" buttons (eg, "OK"). If I don't do that, I just edit everything when the user clicks "OK" or whatever.

      When a "full-screen" edit fails, I display a message and then set the focus to the offending control, so the user does not have to do anything to get to the right place to make a correction.


      MCM
      Michael Mattias
      Tal Systems Inc. (retired)
      Racine WI USA
      [email protected]
      http://www.talsystems.com

      Comment


      • #4
        The actual problem I'm trying to zero in on is when the user has decided to abort the screen altogether. If they click cancel (which only does a DIALOG END CBHNDL) and the field they were in has some data in it, the KILLFOCUS event fires prior to the button click event and actually places them back in the field or the next field if what they entered was ok.

        Anyway, your character by character and/or enabling or disabling control buttons has more promise.

        This program was written by someone else I'm training.

        I'm dealing with specs that say; Control A is the current password field with it own validation in the KILLFOCUS event. It works fine because we pop up a messagebox if it isn't right and this is what allows us to return to Control A and try again. Control B is the New Password field which must abide by rules that say 'numbers and letters require and be a certain length at least. Control C is the Confirm Password field which must remain disabled until a valid Control B (New Password field) has been entered and the user starts to move to the next field. The problem, as you stated, is that the user can also click the Cancel Button Control E. The clicking of Control E while in Control B cranks up the validation routine that is in the KILLFOCUS event of Control B and if what has been entered so far is ok, then there is a Control Focus command to send it to Control C which is the Confirm Password field after enabling it. The click event never fires in this situation.

        Anyway, consider 20 fields, all of whom have validation that is required to be done when the user leaves the field and not character by character. The only routine I've come up with is that if an error in entry occurs during validation in the KILLFOCUS EVENT, to simply set a global flag to the value of the Control ID being left. Also I set a global variable string variable for the error message. In every Set Focus event of every control I add 3 lines that test the value of the global error variable and if it's not 0 then pop up the message. When the message is dismissed then use Control Set Focus back to the offending field. Also if during validation if the result of the entry is ok but requires certain fields to be disabled and a different field be the new target use a negative value for the error control id and this be the next field the cursor should go.

        If I was in charge of specs I would do it the way you are.

        Bob Mechler

        Comment


        • #5
          In this example i capture the tab key and hook the mouse.
          It is still possible to jump to another app but when you restore you'll be prompted with the error.

          http://www.hellobasic.com/trials/uvdtest.exe

          Doubleclick a textbox for adding it to the form and try to modify the nameproperty by adding a numeric in front of the TB's name (like "1TextBox1").
          This is not allowed.

          Code not available (but doable with my hints)
          hellobasic

          Comment


          • #6
            Usually, I don't validate fields until after the user tries to take action (like clicking the OK button). I know this is not the right approach to every application, but I like not interrupting the user more than I have to.
            Erich Schulman (KT4VOL/KTN4CA)
            Go Big Orange

            Comment


            • #7
              Bob,

              In the case you stated, I normally would not have the option buttons part
              of the tab flow. just go from user name to password & back to user name.
              the user can mouse click on the cancel button if he wants. you can check
              the next control in the killfocus routine. if it is the cancel button then quit.
              seems simple to me.

              Comment


              • #8
                Paul,

                In the killfocus event of a textbox, how do you tell what the control or handle of the next location is, as in the case of a user clicking a button which may not be next in the tab order. What method would be used?

                Bob Mechler
                Last edited by BOB MECHLER; 22 Apr 2009, 01:54 PM.

                Comment


                • #9
                  You can get the "next" or "previous" control in the tab order with GetNextDlgTabItem().
                  Michael Mattias
                  Tal Systems Inc. (retired)
                  Racine WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    From api docs..
                    The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus.
                    WM_KILLFOCUS
                    hwndGetFocus = (HWND) wParam; // handle of window receiving focus

                    Parameters
                    hwndGetFocus
                    Value of wParam. Identifies the window that receives the keyboard focus (may be NULL).

                    Return Values
                    An application should return zero if it processes this message.
                    Rgds, Dave

                    Comment


                    • #11
                      Since I know the control Id of the cancel button, having the handle of the control about to get focus GetDlgCtrlID(wparam) can then be verfied as being the cancel button and I can act accordingly.

                      I was used to using the KillFocus event to grab the current control's value and validating it. I just did not think to check the CBWPARAM, or read the manual more closely. My bad

                      Thanks Dave.

                      Bob Mechler

                      Comment


                      • #12
                        This is the part of the manual I was using. The WM_KILLFOCUS event must be used with an SDK control or is the wNoftify Code where to find the control that will get the focus?

                        Can someone clarify?

                        Bob Mechler

                        Code:
                        The EN_KILLFOCUS notification message is sent when an edit control loses the keyboard focus. The parent window of the edit control receives this notification message through the WM_COMMAND message. 
                        
                        EN_KILLFOCUS  
                        idEditCtrl = (int) LOWORD(wParam); // identifier of edit control 
                        wNotifyCode = HIWORD(wParam);      // notification code 
                        hwndEditCtrl = (HWND) lParam;      // handle of edit control 
                         
                        
                        Remarks
                        
                        In previous versions of Windows, the notification code was in HIWORD(lParam). 
                        
                        See Also
                        
                        EN_SETFOCUS, WM_COMMAND

                        Comment


                        • #13
                          To get WM_KILFOCUS from a standard control, you need to subclass.

                          However, some controls - notably the edit (TEXTBOX) and button (BUTTON) controls, send a notification to the owner window. In the case of the edit control, WM_COMMAND/EN_KILLFOCUS is sent; the button sends WM_COMMAND/BN_KILLFOCUS.

                          Let's see if any more controls do this...well, the combobox sends WM_COMMAND/CBN_KILLFOCUS, and the listbox sends WM_COMMAND/LBN_KILLFOCUS.

                          I think that's all the common (not Common) controls which accept input (can receive the keyboard focus).

                          So I guess, the answer is.... while you have to subclass to specifically intercept WM_KILLFOCUS, you do NOT have to subclass to intercept the "event"

                          MCM
                          Michael Mattias
                          Tal Systems Inc. (retired)
                          Racine WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            No-one took a peek at my example app?
                            hellobasic

                            Comment


                            • #15
                              >No-one took a peek at my example app?

                              I think the "Code not available " part may have dampened what I am certain was otherwise exceptional enthusiam for doing so.

                              MCM
                              Michael Mattias
                              Tal Systems Inc. (retired)
                              Racine WI USA
                              [email protected]
                              http://www.talsystems.com

                              Comment


                              • #16
                                It shows that it is certainly possible to write a good validation.
                                To extract code and make it workable for over here would take me time.
                                hellobasic

                                Comment


                                • #17
                                  I took a peek and saw that you were doing what I wanted to do. I can see where you can hook a key and divert it but I wasn't sure about a buttonclick.

                                  There should be a validate event that fires before the kill focus event that would prevent leaving a field until it was right. I had this in Visual FoxPro.

                                  I'm already heading in the direction of subclassing every field that needs validation but just thought with sdk or some other trick I could understand easily there would be a way to do the most fundamental of all form operations. To bulletproof an app, you have to validate fields. It looks like the only way to do it is when you are ready to save the record through the button click event.

                                  Hey maybe I should send a BM_CLICK to a hidden button in the KILLFOCUS event of and control needing validation and validate it there, but I would still not know where it was supposed to go next.

                                  Bob Mechler

                                  Comment


                                  • #18
                                    >thought with sdk or some other trick

                                    There is no elevator to success, there are only the stairs.
                                    Michael Mattias
                                    Tal Systems Inc. (retired)
                                    Racine WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment


                                    • #19
                                      The control is subclassed and uses WM_GETDLGCODE to prevent tab if the validate fails.
                                      On mouse click the mousehook prevents the click on invalid content.
                                      Then there is ALT+TAB (app switching et all) and afaik i used the lostfocus to check the contents.
                                      *After* the msgbox set the focus back so the thing can be validated again.
                                      hellobasic

                                      Comment


                                      • #20
                                        EN_KILLFOCUS fires after the control has lost focus.
                                        WM_KILLFOCUS fires before the control looses focus - you have to subclass a textbox to get at this message though.
                                        See the sample code below for example.
                                        Sample posts messages to the dialog's caption bar as I found that using Message boxes while processing KILLFOCUS
                                        messages could cause disruption to the program flow.
                                        Code:
                                        #INCLUDE "WIN32API.INC"
                                         
                                        %ID_Textbox1 = 1001
                                        GLOBAL OldProc AS LONG 
                                         
                                        FUNCTION PBMAIN () AS LONG
                                         LOCAL hDlg AS DWORD
                                         
                                          DIALOG NEW 0, "Title", , , 240, 150, %WS_CAPTION OR %WS_SYSMENU, 0 TO hDlg
                                            CONTROL ADD LABEL,   hDlg, -1, "Press Tab key or click either button - watch caption for progress", 20, 30, 220, 15
                                            CONTROL ADD TEXTBOX, hDlg, %ID_Textbox1, "TextBox1", 70, 65, 100, 15
                                              OldProc = SetWindowLong (GetDlgItem(hDlg, %ID_Textbox1), %GWL_WNDPROC, CodePtr(SubProc))
                                            CONTROL ADD BUTTON,  hDlg, %IDOK, "OK", 55, 125, 50, 15
                                            CONTROL ADD BUTTON,  hDlg, %IDCANCEL, "Cancel", 135, 125, 50, 15
                                         
                                          DIALOG SHOW MODAL hDlg, CALL DlgProc
                                          SetWindowLong GetDlgItem(hDlg, %ID_Textbox1), %GWL_WNDPROC, OldProc
                                        END FUNCTION
                                        '------------------/WinMain
                                         
                                        CALLBACK FUNCTION SubProc                   ' SubClass proc to catch WM_KILLFOCUS
                                         DIM  Res&
                                          SELECT CASE CBMSG
                                            CASE %WM_KILLFOCUS                      ' Fires before focus moves
                                                Winbeep 800,10
                                              SELECT Case GetDlgCtrlID(CbWParam)    ' ID of next focus 
                                                CASE %IDOK
                                                  Dialog Set Text GetParent(CbHndl), "About to switch focus TO OK Btn"
                                                  Sleep 2000
                                         
                                         '        CASE %IDCANCEL
                                         '          Dialog Set Text GetParent(CbHndl), "About to switch focus TO Cancel Btn"
                                         '          Sleep 2000
                                         
                                                CASE %IDCANCEL
                                                  Res = MsgBox ("Are you sure?", %MB_YESNO Or %MB_ICONQUESTION, "Quit")
                                                  IF Res = %IDYES THEN
                                                    CONTROL POST GetParent(CbHndl), %IDCANCEL, %BM_CLICK, 0, 0  ' reinstate button click
                                                  ELSEIF Res = %IDNO Then
                                                    SetFocus CbHndl
                                                    Function = 0 : Exit Function
                                                  END IF
                                         
                                              END SELECT
                                          END SELECT
                                         FUNCTION = CallWindowProc(OldProc, CbHndl, CbMsg, CbWParam, CbLParam)
                                        END FUNCTION
                                        '------------------/SubProc
                                         
                                        CALLBACK FUNCTION DlgProc() AS LONG
                                         DIM sTemp$ 
                                          SELECT CASE CBMSG
                                            CASE %WM_INITDIALOG
                                              DIALOG SEND CbHndl, %WM_CHANGEUISTATE, MakLng(%UIS_CLEAR, %UISF_HIDEFOCUS), 0
                                         
                                            CASE %WM_COMMAND
                                              SELECT CASE CBCTLMSG
                                                CASE %EN_KILLFOCUS                ' Fires after focus has moved (cues not yet shown though)
                                                  SELECT CASE CBCTL
                                                    CASE %ID_Textbox1                         ' from TextBox1 to..
                                                        Winbeep 400,10
                                                      SELECT CASE GetDlgCtrlID(GetFocus())    ' ID with focus 
                                                        CASE %IDOK
                                                          Dialog Set Text CbHndl, "Focus now on OK Btn - wait 2 sec"
                                                          Sleep 2000
                                                        CASE %IDCANCEL
                                                          Dialog Set Text CbHndl, "Focus now on Cancel Btn - wait 2 sec"
                                                          Sleep 2000
                                                      END SELECT
                                                  END SELECT
                                         
                                                CASE %EN_SETFOCUS
                                                    WinBeep 800,10
                                                  SELECT CASE CBCTL                 ' = Lo(Word, wParam)
                                                    CASE %ID_Textbox1
                                                    Dialog Set Text CbHndl, "Focus now on TextBox"
                                                    Control Redraw CbHndl, %IDOK
                                                  END SELECT
                                         
                                                CASE %BN_CLICKED, 1
                                                  SELECT CASE CBCTL
                                                    CASE %IDOK
                                                      'Control Get Text CbHndl, %ID_Textbox1 To sTemp    ' etc
                                                      Dialog Set Text CbHndl, "" : Sleep 100
                                                      Dialog Set Text CbHndl, "OK button click processed"
                                         
                                                    CASE %IDCANCEL
                                                      Dialog Set Text CbHndl, "Cancel button click processed - 'Bye!"
                                                      Sleep 3000
                                                      DIALOG END CBHNDL, CBCTL
                                                  END SELECT
                                         
                                              END SELECT
                                          END SELECT
                                         
                                        END FUNCTION 
                                        '------------------/DlgProc
                                        Rgds, Dave

                                        Comment

                                        Working...
                                        X