HOWTO: Simulating an Incremental Search from a Text Box

Last reviewed: April 4, 1997
Article ID: Q166378
The information in this article applies to:
  • Microsoft Visual FoxPro for Windows, versions 5.0, 5.0a

SUMMARY

The combo box control has an Incremental Search property that, when set to true (.T.), searches the Control Source of the combo box with each keystroke. Thus, if a user types the string "ABC," the table is searched after each keystroke, first for the letter "A," then the string "AB," then the string "ABC."

While this is a very useful property, the fact that the search is triggered by action rather than inaction causes UI problems--the user may not want to search for each letter of a string as he or she builds it, but rather search for the whole string after it has been fully entered. The text box does not have an incremental search property, but the action can be simulated, and made to search when the user stops typing, rather than when the user is entering the string for which to search.

The code in this article creates a fully editable text box that implements an incremental search procedure. Using a timer, the incremental search is triggered when the user has not pressed a key for a certain timeout period.

MORE INFORMATION

  1. Create a new form, and add the Customer table from the Testdata database (located in the Samples folder) to the DataEnvironment. Drag the table onto the form to create a grid. Add to the form three custom properties: PREVAL, TICKCOUNT, and HOLDVALUE. Change the default values of PREVAL and HOLDVALUE to blank. Change the default value of TICKCOUNT to 0.

  2. Add to the form a custom method named SearchVal. To that method, add the following code:

          * Only seek if the value in the text box has changed
          IF !(THIS.PREVAL == ALLTRIM(THIS.TEXT1.VALUE))
    

            * Set the previous value to the current value
            THIS.PREVAL = ALLTRIM(THIS.TEXT1.VALUE)
    
            * Seek for the text value
            GO TOP
            SEEK THIS.PREVAL
    
            IF FOUND()
               * The custom property holdvalue is used to store the
               * string the user has entered.
               THISFORM.HOLDVALUE = THIS.PREVAL
    
               * This sets focus to the grid so that the located
               * record is displayed in the grid and the
               * RecordMark is turned on for the located record
               THISFORM.GRID1.SETFOCUS
    
               * Reset focus immediately to the text box
               THIS.TEXT1.SETFOCUS
               THIS.TEXT1.VALUE    = THISFORM.HOLDVALUE
               THIS.TEXT1.SELSTART = LEN(THISFORM.HOLDVALUE)
            ENDIF
          ENDIF
    
    

  3. Add a timer to the form, and set the Interval property of the timer to 50. In the Timer event of the Timer control, add the following code:

          THIS.PARENT.TICKCOUNT = THIS.PARENT.TICKCOUNT + 1
    

          IF THIS.PARENT.TICKCOUNT >= 20
    
            THIS.PARENT.TICKCOUNT = 0
            THIS.PARENT.SEARCHVAL()
          ENDIF
    
    

  4. Add a text box to the form. Do not specify a control source for the text box. In the Load event of the form, enter the following command:

          SET ORDER TO CUST_ID && or whatever the name of the index tag
    
                               && for the cust_id field is.
    
    

  5. Add the following code to the text box KeyPress event procedure:

          LPARAMETERS nKeyCode, nShiftAltCtrl
    

          * NOTE: The SHIFT-DIRECTIONKEY highlighting works, but is non
          * standard. For example, SHIFT-LEFT-ARROW highlighting three
          * characters to the right, then hitting the right arrow key, only
          * increments the current position by one. In most editors, doing
          * this would increment the current position by four.  In order to
          * emulate this behavior, however, it would be necessary to save
          * the last key pressed in a variable, then check it to find out
          * in which direction we were last moving, then perform a
          * calculation ... it just didn't seem worth the effort.
    

          LOCAL nPos, nLen, nSel
    

          nPos = THIS.SELSTART         && current pos of the cursor
          nLen = LEN(ALLTRIM(THIS.VALUE))   && length of text in textbox
          nSel = THIS.SELLENGTH         && number of selected chars
    
          THIS.PARENT.TICKCOUNT = 0
    
          * Turn off all default processing of the Keypress Event;
          * this is necessary for code to work
          NODEFAULT
    
          DO CASE
             * Tests/traps for nKeycode >= 65, <= 122 (limits input to
             * alpha characters), nKeycode = 32 (allows for spaces). To
             * allow for input of numerals and/or other characters,
             * refer to the INKEY() topic in the Help file for codes
             * 39 is for single quote
             CASE (nKeyCode >= 65 AND nKeyCode <= 122) OR nKeyCode = 32;
                OR nKeyCode  = 44 OR  nKeyCode  = 45   OR nKeyCode = 39
    
                * Insert the new character in the right place
                THIS.VALUE = UPPER(LEFT(THIS.VALUE, nPos) +   ;
                  CHR(nKeyCode) + RIGHT(THIS.VALUE,;
                  nLen - (nPos + nSel)))
    
                * Increment our position holder so we are placed
                * after the new letter
                nPos = nPos + 1
                nSel = 0
    
             * Tests/traps for the SHIFT-LEFT-ARROW combo (highlight-left)
             CASE nKeyCode = 52
                * Move one to the left, increase number selected by one
                IF  nPos > 0
                   nPos = nPos - 1
                   nSel = nSel + 1
                ENDIF
    
             * Tests/traps for the SHIFT-RIGHT-ARROW combo (highlight-right)
             CASE nKeyCode = 54
                * Increase number selected by one, if there is more to
                * select
                IF (nPos + nSel) < nLen
                   nSel = nSel + 1
                ENDIF
    
             * Tests/traps for the SHIFT-HOME combo (highlight-left-end)
             CASE nKeyCode = 55
                * Set the number of characters selected to the number of
                * characters between our current position and the start
                * position, then move to the start position
                nSel = nSel + nPos
                nPos = 0
    
             * Tests/traps for the SHIFT-END combo (higlight-right-end)
             CASE nKeyCode = 49
                * Set the number of characters selected to the number to
                * the right of our current position
                nSel = nLen - nPos
    
             * Tests/traps for the LEFT-ARROW key
             CASE nKeyCode = 19
                * Deselect any selected characters
                nSel = 0
    
                * Move left one
                IF nPos > 0
                   nPos = nPos - 1
                ENDIF
    
             * Tests/traps for the RIGHT-ARROW key
             CASE nKeyCode = 4
                * Deselect any selected characters
                nSel = 0
    
                * Move right one
                IF  nPos < nLen
                   nPos = nPos + 1
                ENDIF
    
             * Tests/traps for the HOME key
             CASE nKeyCode = 1
                * Deselect any selected characters
                nSel = 0
    
                * Move to the start
                nPos = 0
    
             * Tests/traps for the END key
             CASE nKeyCode = 6
                * Deselect any selected characters
                nSel = 0
    
                * Move to the end
                nPos = nLen
    
             * Tests/traps for the DELETE key
             CASE nKeyCode = 7
                * If we have not 'highlighted' the letter we want to delete,
                * the count of letters to delete will be zero, so we have
                * to manually set the letter to remove
                IF  nSel = 0
                   nSel = 1
                ENDIF
    
                * Rebuild the string without the deleted character(s)
                THIS.VALUE = LEFT(THIS.VALUE, nPos) + ;
                    RIGHT(THIS.VALUE, nLen - (nPos + nSel))
    
                * Deselect any selected characters
                nSel = 0
    
             * Tests/traps for the BACKSPACE key
             CASE nKeycode = 127
                * If no letters are 'highlighted', just delete the one
                * before the cursor and
                * move the cursor back one
                IF nSel = 0
                   IF  nPos > 0
                      THIS.VALUE = LEFT(THIS.VALUE, nPos - 1) + ;
                        RIGHT(THIS.VALUE, nLen - nPos)
                        nPos = nPos - 1
                   ENDIF
    
                   * If letters are highlighted, remove the block of
                   * highlighted letters, and don't move the cursor
                   * (just like hitting DELETE)
                ELSE
                   THIS.VALUE = LEFT(THIS.VALUE, nPos) + ;
                      RIGHT(THIS.VALUE, nLen - (nPos + nSel))
                ENDIF
    
                * Deselect any selected characters
                nSel = 0
    
          ENDCASE
    
          * Get rid of whitespace at the end of the entered word
          IF nLen != LEN(ALLTRIM(THIS.VALUE))
             THIS.MAXLENGTH = LEN(ALLTRIM(THIS.VALUE)) + 1
          ENDIF
    
          * This moves the cursor to the current position in the string in the
          * text box
          THIS.SELSTART  = nPos
    
          * And this selects the proper number of characters
          THIS.SELLENGTH = nSel
    
    

  6. Save and run the form. Type a search field into the text box. After a brief pause, any matching fields should be located within the database and pointed to on the Grid. Experiment with different timeout periods until you find one with which you are comfortable.

General Notes

  • In this example it is assumed that the text box is named Text1. If the name of that text box is different, modify the code accordingly.
  • The NODEFAULT command turns off all of the default processing of the KeyPress event and allows the code to completely control the processing of the key presses. This command is necessary.
  • As is noted in a few of the comments, the edit emulation is not perfect, but it is fairly close to target.

(c) Microsoft Corporation 1997, All Rights Reserved. Contributions by Robert Mobbs, Microsoft Corporation.


Keywords : FxprgClassoop FxprgGeneral FxtoolFormdes kbtool vfoxwin
Version : 5.0 5.0a
Platform : WINDOWS
Issue type : kbhowto


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: April 4, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.