Announcement

Collapse
No announcement yet.

Trap left click in Superclassed Edit control

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

  • Michael Mattias
    replied
    >Can a call back function be in a DLL if the Dialog is created in the main exe?

    Not directly using DDT syntax, since DDT requires the CALL procedurename within the current code module.


    But you could go the indirect route...

    Code:
    DECLARE FUNCTION RealCallBack  LIB "MyDLL.DLL" _
        (BYVAL hwnd AS LONG, _
         ByVAL usMSG AS LONG, 
         BYVAL wParam AS LONG, _ 
         BYVAL lParam AS LONG) AS LONG 
    
    
      DIALOG NEW ....
    
      DIALOG SHOW hDlg.... CALL FunctionHere
    
    CALLBACK FUNCTION FunctionHere () AS LONG
        FUNCTION = RealCallBack (CBHNDL, CBMSG, CBWPARAM, CBLPARAM) 
    END FUNCTION
    With SDK-style coding, you don't need the extra function since you supply the address, not the name, of the window or dialog procedure.

    I'll guess in your window procedure in the DLL, you could just code it as
    Code:
    CALLBACK FUNCTION  RealCallback  () EXPORT AS LONG 
    
    END FUNCTION
    ... and all your "CBxxxxx" system constants should still work.

    (If this is not doable it certainly should be)

    MCM
    Last edited by Michael Mattias; 1 Aug 2008, 11:52 AM.

    Leave a comment:


  • BOB MECHLER
    replied
    We do some 'on demand' loading and freeing of customer specific dynamic link libraries as well as some load on first use and leave loaded DLL's.

    If the client id is such and such we load their library and call the functions there instead of the default functions, etc.

    If I used dll's for the 'hard stuff' I'd need to put all the gui in the DLL, right?

    Can you have the parameters defined in the main exe and create the GUI entirely in the DLL. Hmmm. This would keep the INCLUDE files from being listed in every EXE.

    Can a call back function be in a DLL if the Dialog is created in the main exe?

    Bob Mechler

    Leave a comment:


  • Michael Mattias
    replied
    .....used to most of the hard stuff being hidden
    ... a standard look and feel, a standard way to open and access files and a standard way to process things. While uninteresting, it gets programs out the door.
    Sounds to me like an ideal situation for giving these programmers a little documentation and a header file pointing to a dynamic link library.

    Leave a comment:


  • BOB MECHLER
    replied
    If it was just me, I'd do it the way you do. I have to deal with 5 other programmers who aren't api programmers. They are application programmers and used to most of the hard stuff being hidden. They like 'Access' (shudder).

    Also to have most functions and business logic in superclassed controls and include files that only I and one other programmer deal with makes development by the other programmers very quick. We have a standard look and feel, a standard way to open and access files and a standard way to process things. While uninteresting, it gets programs out the door.

    Bob Mechler

    Leave a comment:


  • Michael Mattias
    replied
    With Control Add "Classname" I can do the following without putting code in a dialog callback under wm_command [italics mine. MCM]
    A-ha! This explains much.

    I remember when DDT first came out, I thought the control level callbacks ("CONTROL ADD... CALL functionname") would turn out to be the greatest thing since sliced bread and would be really handy to make highly-maintainable code.

    I gave it a shot, but discovered I felt a lot more comfortable with all the handlers located in one procedure... the dialog procedure. Plus, the initial release of DDT did not offer the 'CONTROL SET USER' features, and I was really used to using all the controls' "DWL_USER" or "GWL_USERDATA" integer, and the only way at that time to store this kind of data and access it in the separate control procedures was via (ugh!) GLOBAL variables.

    In addition, I had learned Windows programming with PB/DLL before there was such a thing as 'DDT,' so I was used to having all the code for one screen in one procedure.

    I guess if you start programming Windows' applications using 'DDT' with 'CONTROL SET USER' capability, it's a lot different.

    MCM

    Leave a comment:


  • BOB MECHLER
    replied
    Why superclass?
    Well,


    With Control Add "Classname" I can do the following without putting code in a dialog callback under wm_command

    Navigate using Up,Down,Tab,Shift Tab, and Return and validate the field prior to moving to next or previous control.

    I can trap Ctrl-F and run another program while the current program is frozen in a loop waiting for a return value from a customized lookup exe that itself has the ability to call another maintenance program and add new items to the lookup.

    I can validate an ACCOUNT NUMBER or other code by looking it up in a table as part of the validation in the Superclassed control. If it is not found I just clear the field.

    In one of the superclassed controls I can allow or restrict any set of characters with a different set of allowable characters for each position.

    I can activate Insert or Overwrite mode in the textbox.

    I can format and unformat code as I enter or leave the control.

    In PBMAIN I fill several arrays with Captions, Control Type with switches making the control required or not, buddy lookup buttons and optional description fields as well as the screen it's on, the tab it's on if any, the group it's in, the position, width height, font, color

    The id of the main superclassed control is used by the arrays and buddy controls are offsets to this control id so that I can update a description control in the superclassed control because I know what it's control id must be.

    The ultimate reason is that a field for data input needs to behave the same way in all programs it's used in. Every field has field edit rules which used to be repeated (or not) in every program they were used in. This way the SuperClassed control include files nail down the business rules for that field. If they change, we don't have to search for every occurance of the field in use. Just recompile the programs in our main offering.

    I hope to keep the Dialog callback to handling menu items in WM_COMMAND, WM_DESTROY and WM_NOTIFY, WM_DRAWITEM and that's about it.

    The programmer then only needs one line of code to add all this functionality to the program.

    Imagine a Listview superclass that automatically loads data from a pre-defined file relationship and presents a detail modification screen by double clicking on the row and has an incremental search function build in with one line of code.

    In reviewing POFFS, I noticed many instances where the same functionality was programmed umpteen different ways.

    In my own code, I was doing the same type of the thing over and over again only every time I did it I made it a little better or added features. This way if I improve how a CUSIP is looked up, verified, that improvement will flow to all the other programs through a recompile.

    Based on Edwin's advice I've removed any set focus processing in the wm_killfocus event

    Time to get off my soapbox.

    The following code is not complete but illustrates a superclassed account number field that does all the things mentioned.

    Code:
       GLOBAL F_F1 AS LONG
    
       FUNCTION NEditActnrF1Lookup(txtACTNR AS STRING,FileEquate AS LONG) AS STRING
         BTRVSETUP                                               
         QF = 5: Q = FileEquate: Q$(Q) = TRIM$(txtACTNR): QNUM = 0: GOSUB 60000
         IF QSTATUS = 0 THEN  
           sTxt = F1.ACTNR$         
         ELSE
           sTxt = "" 
         END IF 
         FUNCTION = sTxt
       EXIT FUNCTION
       60000   #INCLUDE "BTRVCALX.INC"
       END FUNCTION          
    
       CALLBACK FUNCTION NEditActnrProc
          STATIC OldProc AS LONG, OffsetWndExtra AS LONG
          LOCAL V_NKEYPOS AS LONG,sTXT AS STRING,Txt AS ASCIIZ * %MAX_PATH, zfName AS ASCIIZ * %MAX_PATH
          LOCAL ValidActnrKeys AS STRING,FieldId as LONG
          LOCAL ActnrID AS LONG, DescID AS LONG, DescHandle AS LONG
          DIM  Tlen AS LOCAL LONG
          ValidActnrKeys$ = " 0123456789" + CHR$(8)
          ValidActnrKeysAbbrev$ = "Entry must be 7 digits [0-9]"
          IF CBHNDL = 0 THEN OldProc = CBWPARAM: OffsetWndExtra = CBLPARAM: EXIT FUNCTION
    
          'Control Id and Handle of ACTNR
          ActnrID& = getdlgctrlid(CBHNDL)
    
          'Control Id and Handle of Read Only field displaying the F1.ACTNAM
          DescID& = ActnrID& + scr_NUM_FLD#
          DescHandle& = GetDlgItem(GetParent(CBHNDL),DescID&)
          
          SELECT CASE CBMSG
             CASE %WM_GETDLGCODE
                FUNCTION = %DLGC_WANTALLKEYS: EXIT FUNCTION
             CASE %WM_KEYUP
               CONTROL SEND GetParent(CBHNDL),ActnrID&, %EM_GETSEL, 0, 0 TO V_NKEYPOS&     'Used to detect when field is filled           
               IF LOWRD(V_NKEYPOS&) = FieldLen(ActnrID&) THEN
                 GetWindowText CBHNDL, Txt, %MAX_PATH
                 sTxt = Txt
                 sTxt = NEditActnrF1Lookup(sTxt,F_F1&) 'Returns the Account or blank if it is not on file
                 IF LEN(TRIM$(sTxt)) = 0 THEN    
                  'ShowBallonText GetParent(CBHNDL), CBHNDL, "Account is not on file"
                   'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_SHOW
                  ' CONTROL SET TEXT GetParent(CBHNDL),%ER_MSG,"Account is not on file"
                   'SLEEP 1000                                           
                   'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_HIDE             
                   'EXIT FUNCTION ' stay in field until a valid CUSIP is found or the user presses enter with the field empty
                 END IF
                 SetWindowText CBHNDL, BYCOPY F1.ACTNR
    
                 SetWindowText DescHandle&, BYCOPY F1.ACTNAM  
                 SetFocus GetNextDlgTabItem(GetParent(CBHNDL), CBHNDL, 0) 'all ok, move to the next field
                 EXIT FUNCTION
               END IF
               IF GetWindowTextLength(CBHNDL) = 0 THEN 'handles the case where the field contents become zero during editing
                 SetWindowText DescHandle&,""
               END IF
             CASE %WM_KEYDOWN
               SELECT CASE CBWPARAM
                 CASE %VK_UP,%VK_DOWN,%VK_RETURN,%VK_TAB  
                   Tlen = GetWindowTextLength(CBHNDL) 'get the text length
                   IF Tlen = FieldLen(ActnrID&) THEN
                     GetWindowText CBHNDL, Txt, %MAX_PATH
                     sTxt = Txt
                     sTxt = NEditActnrF1Lookup(sTxt,F_F1&)  
                     IF LEN(TRIM$(sTxt)) = 0 THEN  'Field however modified is valid, fill in the description field also   
                       'ShowBallonText GetParent(CBHNDL), CBHNDL, "Account is not on file"               
                       'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_SHOW
                       'CONTROL SET TEXT GetParent(CBHNDL),%ER_MSG,"Account is not on file"
                       'SLEEP 1000                                           
                       'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_HIDE             
                       SetWindowText CBHNDL, ""
                       SetWindowtext DescHandle&,""               
                       'EXIT FUNCTION ' stay in field until a valid CUSIP is found or the user presses enter with the field empty
                     ELSE 'Field however modified is valid, fill in the description field also
                       SetWindowText CBHNDL, BYCOPY F1.ACTNR
                       SetWindowText DescHandle&, BYCOPY F1.ACTNAM                      
                     END IF
                   ELSE  
                     'ShowBallonText GetParent(CBHNDL), CBHNDL, "Account field is required"
                     'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_SHOW
                     'CONTROL SET TEXT GetParent(CBHNDL),%ER_MSG,"Account field is required"
                     'SLEEP 1000                                           
                     'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_HIDE             
                     'EXIT FUNCTION 'stay in field until there is a valid account 
                   END IF
                   IF CBWPARAM = %VK_UP OR (CBWPARAM = %VK_TAB AND GetAsyncKeyState(%VK_SHIFT)) THEN  
                     SetFocus GetNextDlgTabItem(GetParent(CBHNDL), CBHNDL, -1)
                   ELSE
                     SetFocus GetNextDlgTabItem(GetParent(CBHNDL), CBHNDL, 0)
                   END IF  
                   EXIT FUNCTION
                 CASE %VK_F 'find routine
                   IF GetAsyncKeyState(%VK_CONTROL) THEN
                     LSET C.INQ = "": LSET C.CSORT = CSORT$
                     SPARE$ = CPROGID$: LSET C.SPARE = SPARE$: C.LSPARE = LEN(SPARE$)': @pC = C
                     PU_PROG$ = CPROGID$: LSET C.PU_PROG = PU_PROG$: @pC = C
                     IF FNSP(CUST_DIR$) THEN
                       SHELLPROG$ = "PUACCT.EXE "
                     ELSE
                       SHELLPROG$ = $STDEXE + "PUACCT.EXE "
                     END IF
                     DIALOG DISABLE GetParent(CBHNDL)
                     Yinstance& = SHELL(SHELLPROG$ + COMMONLINK$)
                     SLEEP 1000
                     Zprocessid& = OpenProcess(%PROCESS_QUERY_INFORMATION + %PROCESS_TERMINATE,%False,Yinstance&)
                     DO
                       DIALOG DOEVENTS
                       I& = GetExitCodeProcess(BYVAL Zprocessid&,lpExitCode&)
                     LOOP WHILE lpExitCode& = %STILL_ACTIVE
                     DIALOG ENABLE GetParent(CBHNDL)
                     DIALOG SHOW STATE hdlg,%SW_SHOW
                     stat = GetCommon(C)
                     INQACTNR$ = RTRIM$(INQ$)
                     sTxt = INQACTNR$  'same as if NEditActnrF1Lookup had been called
                     IF LEN(TRIM$(sTxt)) = 0 THEN 'lookup cancelled go with field contents
                       GetWindowText CBHNDL, Txt, %MAX_PATH
                       sTxt = Txt
                     END IF
                     sTxt = NEditActnrF1Lookup(sTxt,F_F1&)  
                     IF LEN(TRIM$(sTxt)) = 0 THEN  'Field however modified is valid, fill in the description field also   
                       'ShowBallonText GetParent(CBHNDL), CBHNDL, "Account is not on file"               
                       'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_SHOW
                       'CONTROL SET TEXT GetParent(CBHNDL),%ER_MSG,"Account is not on file"
                       'SLEEP 1000                                           
                       'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_HIDE             
                       SetWindowText CBHNDL, ""
                       SetWindowtext DescHandle&,""               
                       'EXIT FUNCTION ' stay in field until a valid account is found
                     ELSE 'Field has a valid value from the lookup, fill fields and proceed to the next tab stop
                       SetWindowText CBHNDL, BYCOPY F1.ACTNR
                       SetWindowText DescHandle&, BYCOPY F1.ACTNAM                      
                       SetFocus GetNextDlgTabItem(GetParent(CBHNDL), CBHNDL, 0)
                       EXIT FUNCTION
                     END IF
                   END IF  
                 CASE %VK_INSERT
                   IF NOT V_INSOVR THEN
                     V_INSOVR = -1
                   SetWindowText GetParent(CBHNDL),"OVR"   
                   ELSEIF V_INSOVR THEN
                     V_INSOVR = 0
                   SetWindowText GetParent(CBHNDL),"INS"   
                   END IF
               END SELECT
             CASE %WM_CHAR
               IF INSTR(ValidActnrKeys$,CHR$(CBWPARAM)) = 0 THEN
                 IF CBWPARAM > 32 THEN 
                   ShowBallonText GetParent(CBHNDL), CBHNDL, ValidActnrKeysAbbrev$
                   'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_SHOW
                   'CONTROL SET TEXT GetParent(CBHNDL),%ER_MSG,ValidActnrKeysAbbrev$
                   'SLEEP 1000                                           
                   'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_HIDE             
                 END IF  
               END IF  
               IF INSTR(ValidActnrKeys$,CHR$(CBWPARAM)) = 0 THEN EXIT FUNCTION
               KeyState = GetAsyncKeyState(%VK_OEM_MINUS) OR GetAsyncKeyState(%VK_OEM_PLUS)
               IF INSTR("+-",CHR$(CBWPARAM)) > 0 AND KeyState <> &H8001 THEN EXIT FUNCTION
               IF V_INSOVR THEN
                 SendMessage CBHNDL, %EM_GETSEL, 0, 0 TO V_NKEYPOS&     'Used to detect when field is filled           
                 V_NKEYPOS& = LOWRD(V_NKEYPOS&)
                 SendMessage CBHNDL, %EM_SETSEL,V_NKEYPOS&,V_NKEYPOS& + 1  'Selects current character
                 SendMessage CBHNDL, %WM_CLEAR,0,0                       'If nothing to clear doesn't act
               END IF
             CASE %WM_SETFOCUS
               SendMessage CBHNDL, %EM_SETSEL, 0, -1 
             CASE %WM_KILLFOCUS 
    '           Tlen = GetWindowTextLength(CBHNDL) 'get the text length 
    '           IF Tlen < FieldLen(ActnrID&) AND Tlen > 0 THEN
    '             ShowBallonText GetParent(CBHNDL), CBHNDL, "WARNING! Account was not found."
    '           ELSEIF Tlen = 0 THEN
    '             ShowBallonText GetParent(CBHNDL), CBHNDL, "WARNING! Account is required."                            
    '           END IF
                 'SetFocus CBHNDL 
    '             ShowBallonText GetParent(CBHNDL), CBHNDL, "Account not found"               
    '             DIALOG GET SIZE GetParent(CBHNDL) TO tot_x&,tot_y&
    '             DIALOG GET CLIENT GetParent(CBHNDL) TO cli_x&,cli_y&
    '             DIALOG UNITS GetParent(CBHNDL),tot_x& - cli_x&,tot_y& - cli_y& TO PIXELS non_client_x&,non_client_y&
    '             DIALOG GET LOC GetParent(CBHNDL) TO MOUSE_X&,MOUSE_Y&
    '             DIALOG UNITS GetParent(CBHNDL), MOUSE_X&,MOUSE_Y& TO PIXELS P_MOUSE_X&,P_MOUSE_Y&
    '             CONTROL GET LOC GetParent(CBHNDL),ActnrID& TO TV_X&,TV_Y&
    '             DIALOG UNITS GetParent(CBHNDL), TV_X&,TV_Y& TO PIXELS P_TV_X&,P_TV_Y&
    '             SetCursorPos P_MOUSE_X& + P_TV_X& + 10, P_MOUSE_Y& + P_TV_Y& + non_client_y& + 9             
    '             EXIT FUNCTION 'stay in field until there is a valid account
    '           ELSEIF Tlen = 0 THEN
    '             ShowBallonText GetParent(CBHNDL), CBHNDL, "Account field is required"               
    '           END IF
          END SELECT
          FUNCTION = CallWindowProc(OldProc, CBHNDL, CBMSG, CBWPARAM, CBLPARAM) 
             
       END FUNCTION
    
       CALLBACK FUNCTION NActnrButtonProc
          STATIC OldProc AS LONG, OffsetWndExtra AS LONG
          LOCAL Txt AS ASCIIZ * %MAX_PATH, sTxt AS STRING
          LOCAL ActnrButtonID AS LONG, ActnrID AS LONG, DescID AS LONG
          LOCAL ActnrHandle AS LONG, DescHandle AS LONG
          IF CBHNDL = 0 THEN OldProc = CBWPARAM: OffsetWndExtra = CBLPARAM: EXIT FUNCTION
          ActnrButtonID& = getdlgctrlid(CBHNDL)
          ActnrID& = ActnrButtonID& - (scr_NUM_FLD# * 2 )
          DescID& = ActnrButtonID& - scr_NUM_FLD#
          ActnrHandle& = GetDlgItem(GetParent(CBHNDL),ActnrID&)
          DescHandle& = GetDlgItem(GetParent(CBHNDL),DescID&)
          SELECT CASE CBMSG
            CASE %WM_LBUTTONDOWN
               LSET C.INQ = "": LSET C.CSORT = CSORT$
               SPARE$ = CPROGID$: LSET C.SPARE = SPARE$: C.LSPARE = LEN(SPARE$)
               PU_PROG$ = CPROGID$: LSET C.PU_PROG = PU_PROG$: @pC = C
               IF FNSP(CUST_DIR$) THEN
                 SHELLPROG$ = "PUACCT.EXE "
               ELSE
                 SHELLPROG$ = $STDEXE + "PUACCT.EXE "
               END IF
               DIALOG DISABLE GetParent(CBHNDL)
               Yinstance& = SHELL(SHELLPROG$ + COMMONLINK$)
               SLEEP 1000
               Zprocessid& = OpenProcess(%PROCESS_QUERY_INFORMATION + %PROCESS_TERMINATE,%False,Yinstance&)
               DO
                 DIALOG DOEVENTS
                 I& = GetExitCodeProcess(BYVAL Zprocessid&,lpExitCode&)
               LOOP WHILE lpExitCode& = %STILL_ACTIVE
               DIALOG ENABLE GetParent(CBHNDL)
               DIALOG SHOW STATE hdlg,%SW_SHOW
               stat = GetCommon(C)
               INQACTNR$ = RTRIM$(INQ$)
               sTxt = INQACTNR$
               IF LEN(TRIM$(sTxt)) = 0 THEN
                 GetWindowText ActnrHandle&,Txt,%MAX_PATH
                 sTxt = Txt
               END IF      
               sTxt = NEditActnrF1Lookup(sTxt,F_F1&)
               IF LEN(TRIM$(sTxt)) = 0 THEN  'Field however modified is valid, fill in the description field also   
                 ShowBallonText GetParent(CBHNDL), ActnrHandle&, "Account is not on file"               
                 'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_SHOW
                 'CONTROL SET TEXT GetParent(CBHNDL),%ER_MSG,"Account is not on file"
                 'SLEEP 1000                                           
                 'CONTROL SHOW STATE GetParent(CBHNDL),%ER_MSG,%SW_HIDE             
                 SetWindowText ActnrHandle&, ""
                 SetWindowtext DescHandle&,""               
                 EXIT FUNCTION ' stay in field until a valid account is found
               ELSE 'Field has a valid value from the lookup, fill fields and proceed to the next tab stop
                 SetWindowText ActnrHandle&, BYCOPY F1.ACTNR
                 SetWindowText DescHandle&, BYCOPY F1.ACTNAM 
                 SetFocus GetNextDlgTabItem(GetParent(CBHNDL),ActnrHandle&, 0)
                 EXIT FUNCTION
               END IF
          END SELECT
          FUNCTION = CallWindowProc(OldProc, CBHNDL, CBMSG, CBWPARAM, CBLPARAM)
       END FUNCTION
    Some code not shown that does the actual superclass creation. There is code from a lot of people in here. Semen's Superclass creation code is elsewhere. A lot of this has been trial and error.

    Bob Mechler

    Bob Mechler

    Leave a comment:


  • Michael Mattias
    replied
    I don't understand the preoccupation with "superclassed"

    That's really immaterial. Either your "superbutton" sends a WM_COMMAND notification of significant events to its owner or you deliberately (or accidentally?) ate the initiatiing event in your window procedure. Code not shown.

    MCM

    Leave a comment:


  • Edwin Knoppert
    replied
    >or I am just being hard-headed?
    Hmm.. yes, but you are simply doing what so many had to do before.
    I had this discusion for hours if not days to explain we are dealing with Windows, not DOS.
    (It was just before Win95 was out )
    I wrote some code and worked annoyingly and unpredictable.
    Though i was using VB3 at that time, i didn't had a hooks and so.
    Later i tried with PB/DLL 2.0 and discovered it went nowhere.

    The killfocus and setfocus messages are very low-level and textbox carets will suffer if you aren't doing it right.
    What other (future) control do you need to take of..?)

    Leave a comment:


  • BOB MECHLER
    replied
    Validate under an OK button would normally be a form validation kind of thing. Very safe.

    Would there be anything troublesome about sending %BM_CLICK to a validate button control from the %WM_KILLFOCUS event of a superclassed control and let the callback for the button determine where to SetFocus? Either back to the original if the validation fails or the next tabstop.

    Think I'll try that approach, or I am just being hard-headed?

    Bob Mechler

    Leave a comment:


  • Edwin Knoppert
    replied
    I think VFP wasn't that good actually but you may had a GUI not being bothered with side effects.

    For tab key you may consider the WM_GETDLGCODE message.
    There is no just before lost focus message.
    There is a WM_MOUSEACTIVATE which may be useful.
    Both these messages are working recursive, first control, then the window receives it (unless prevented and i may be wrong in the order)

    But still, i am sure you'll encounter issues.
    Like two textboxes where the validate decides empty is not good and keeps jumping between these controls (due setfocus())

    Validate should be under a OK button.

    Leave a comment:


  • BOB MECHLER
    replied
    Thanks Edwin. That would explain some things I've started to notice when going from field to field tightly controlling navigation with tests in the vk_up,vk_down, etc and wm_killfocus event. I could force the focus to stay in the control by just not issuing the code section:
    Code:
                   IF CBWPARAM = %VK_UP OR (CBWPARAM = %VK_TAB AND GetAsyncKeyState(%VK_SHIFT)) THEN  
                     SetFocus GetNextDlgTabItem(GetParent(CBHNDL), CBHNDL, -1)
                   ELSE
                     SetFocus GetNextDlgTabItem(GetParent(CBHNDL), CBHNDL, 0)
                   END IF  
                   EXIT FUNCTION
    except in WM_KILLFOCUS. However that was the only way I could figure out how to trap the fact that someone was trying to jump the gun and click in another field with the current field not validated.

    Is is possible to trap the buttonclick before the WM_KILLFOCUS event fires. I remember Visual Foxpro had a validate event ( or ACCESS) that fired prior to the Lost Focus event. That's what I really need here.

    Bob Mechler

    Leave a comment:


  • Edwin Knoppert
    replied
    I have experiance with this.
    Avoid testing on lost focus and then restore focus.
    It will never work properly icw other controls.

    Leave a comment:


  • BOB MECHLER
    replied
    Excellent code for a non-superclassed control. I guess I'd use the WM_CHAR event in a superclassed control, wouldn't I or is the %EN_CHANGE event available in a superclassed control, don't know, haven't tried that.

    Bob Mechler

    Leave a comment:


  • Michael Mattias
    replied
    Or.... you can check entries character by character using EN_CHANGE notification....

    ... this won't 'stay in the field' but if there is a "do it" button you enable/disable based on the user having put something valid in some box, I always liked this particular effect. Button enables/disables 'as you type.'

    Code:
          CASE %ID_FILENAME
                  ' check if the file is avaiable and no worker thread is running already,
                  ' if so enable the start button
                  [b]IF CBCTLMSG = %EN_CHANGE THEN[/b]
                     DIALOG GET USER CBHNDL, %DSU_HTHREAD TO hThread
                     IF ISFALSE hThread THEN    ' no worker thread is running, so we can enable
                                                ' the 'go' button if this file is valid
                          CONTROL GET TEXT CBHNDL, %ID_FILENAME TO sText
                          IF DIR$ (sText) > "" THEN
                               CONTROL ENABLE CBHNDL, %ID_START
                          ELSE
                               CONTROL DISABLE CBHNDL, %ID_START
                          END IF
                     END IF
                  END IF
    Full demo here...
    http://www.powerbasic.com/support/pb...ad.php?t=35619


    MCM

    Leave a comment:


  • BOB MECHLER
    replied
    Correct. I had two copies of the same include. One didn't have anything in the WM_KILLFOCUS event at all and the other one did. I was compiling in the wrong one. Duh. Sorry.

    Code:
             CASE %WM_KILLFOCUS 
               Tlen = GetWindowTextLength(CBHNDL) 'get the text length
               IF Tlen < FieldLen(getdlgctrlid(CBHNDL)) THEN
                 ShowBallonText GetParent(CBHNDL), CBHNDL, "Account field is required"
                 SetFocus CBHNDL
                 EXIT FUNCTION 'stay in field until there is a valid account
               END IF

    Leave a comment:


  • Fred Harris
    replied
    Wm_killfocus ?

    Leave a comment:


  • BOB MECHLER
    started a topic Trap left click in Superclassed Edit control

    Trap left click in Superclassed Edit control

    What event do I trap in a SuperClassed edit box to keep it from losing focus until it matches a validation if the user clicks in another field?

    Bob Mechler
Working...
X