Foxite.COM Community Weblog

Foxite.COM Community Weblog - free weblog service for the Visual FoxPro Community.
Welcome to Foxite.COM Community Weblog Sign in | Join | Help
in
Home Blogs Forum Photos Forum Archives

Ammar Hadi



  • Flexible Smart Barcode Scanning


    Hi Friends ..

    I've noticed that many POS applications that allow the use of a Barcode Reader device is designed in a way so that there is a textbox on the pos window that is supposed to be used to receive the scanned barcode.

    This approach has a main crippling feature. The Barcode TextBox should be the one with the focus before scanning the barcode, otherwise the loaded barcode characters will be useless or may mess around other controls.

    Another feature is that in most of the Barcode Readers the default Suffix (the last character in the loaded barcode) is {Enter} key + a Line Feed, or a suffix of {Tab} character. This will result in that after reading the code, the Barcode textbox will lose the focus.

    Therefor, developers play around these features to Keep the barcode textbox always in focus to ensure correct input and at the same time add codes to keep it focused after reading the code, to allow for the next item barcode be loaded correctly.

    I believe that this way of development produce a less flexible interface and less user friendly.

    My Approach:

    The way I deal with the use of barcode readers to have a more flexible (and smartly behaving) application is based on the following:

    1) The Configuration of the Barcode Reader:

    The Barcode Reader has a vast number of configurable parameters (independent on the computer). Each type of barcode readers has a user manual that contain the codes to program and configure the barcode reader. So that the reader is configured just by scanning some special barcode codes that the reader identify as a configuration process. Of these parameters, two I used to apply my idea: The
    Prefix and the Suffix.

    A common default way of loading the barcode is like this example:

    actual barcode:
    6281101220014 (Small Bottled Water Smile [:)])
    the sent code: 
    6281101220014{ENTER}+Line Feed or 6281101220014{TAB}

    (This is just to represent what is received, it is not actual code, the actual code is every number is send as ASCII character and added to it ASCII char 13 (for Enter) + char 10 (for Line Feed), or 9 (for Tab))

    My idea will be based on Adding a predefined prefix to the loaded barcode (this is done by the barcode reader) so that this prefix will be trapped by the form and after which the ASCII codes received will be regarded as the actual barcode. Then comes the suffix (I use the TAB), so when the form trap a TAB while it is in the mode of reading the barcode, it will consider that the barcode string is complete and ready to be processed.

    The selected ASCII character to be a prefix could be an uncommonly used printable ASCII character or a non-printable character (ASCII codes 0-31 non-printable). This selection depends on the application type, and other factors related to the developer and users so that the prefix selected is not to be used in the regular keyboard work. And the user can be notified not to use that character in his regular data entry.

    In the example form I attached, I used the
    # char chr(35) as a prefix. Its just as an example, you should choose what suits your work.

    As an example of how to set up a barcode reader to be compatible with this approach, the picture below show the codes (extracted from the installation manual) to configure a
    Metrologic MS9520/40 Voyager® Series barcode reader for this purpose.



    Manhattan Barcode Scanner
    is another type that I found configurable but with a different sequence of codes. So, it depends on the type and manufacturer of the scanner.

    2) The Design of the Form:

    What we need here is that the form should be the one which receives and manages the barcode, not the textbox. And we need to add some custom properties and methods to the form to organize our work and add this new functionality to the form.

    The custom properties added are:

    Barcode_firstchar: integer value representing the ASCII code of the prefix (in the example form, it is defaulted to 35 which is the # character)

    Barcode_lastchar: integer value representing the ASCII code of the suffix (in the example form, it is defaulted to
    9 which is the TAB character)

    Reading_Barcode: logical value, the default is .F. used to indicate whether we are in the barcode reading mode or not. setting it to .T. will direct the keys in the keyboard buffer to build the barcode string till the Barcode_lastchar is passed.

    BarcodeString: this character value will save the barcode while we are in the barcode reading mode.

    The custom
    methods:

    Build_barcode: this method will recieve the keycode from the keypress event of the form during the barcode reading mode and add it to the barcodestring.

    Manage_barcode: This method is used to manage the received barcode after completion. Here in this method the flexibility and smart barcoding is managed.
    In the example attached, the form has a pageframe with 3 pages of different uses. One to simulate entry of a new item. the second to simulate finding an item by reading its barcode. the third is to simulate entering invoice items using the barcode reader.
    Manage_barcode will see which is the current active page to perform the proper action.
    So, if it is the first page (new item) the code loaded will be shown in the barcode textbox of the new item.
    if the second page (select Item), the loaded barcode will be searched in the available item barcodes in the items table. If it is found, its data will be shown. If not, a notification messagebox appear to declare that this item is not identified.
    If it is the Third page (new invoice), the loaded barcode will be searched in the items table. If
    it is available it will be inserted into the new invoice (or the quantity is increased by 1 if it is already in the invoice). If not available a messagebox will be shown to declare that this item is not identified.

    In addition to these properties and methods, the form
    KeyPreview property should be set to .T. and the form Keypress event will contain a control code to re-direct the keycode to build the barcode string during barcode reading mode and suppress the default behavior of the key.

    To Test the sample, you either can add real item barcodes to the items table, then run the form and use your configured barcode scanner. Or, you can use the keyboard to simulate the process by pressing # to load the prefix, then as you enter the characters that represent an available barcode, the form will be in the reading mode. Then press TAB key to load the suffix character. At that time you will get the appropriate response to the entered barcode.

    This way .. you will have a smart form that knows what to do with the loaded barcode wherev
    er the current focus is because it is form-level activity not control-level.

    And with a creative mind you can do any action based on the read code and the current form status like calling a new-item form when the loaded barcode is not identified .. or issue some control barcodes .... etc.

    I hope these ideas a
    re helpful

    NOTES:

    * some barcode scanners allow for a longer prefix with 2 or more characters.
    * this barcode functionality can be saved into a custom class or a form class.
    * the example I posted is not fault proof and I just post it to show the basic idea and the used code.
    * the prefix and suffix in a real application can be stored in the configuration table so that they can be part of the application configuration and customization process.

    The Attachment:

    here is the sample form and table. (Run the form and it will automatically locate the items table included in the same folder):

    Download The Sample

    ----------------------------------------------------

    Ammar Hadi ... 22/8/2009

  • Undo Redo



    Hi Friends ...

    Here is a way I used to add an undo - redo functionality to applications.

    The Basic Idea:

      The basic idea I used is that, with every change, I save the code script that will undo it. And save the code script that will redo it in a table that will save these scripts ordered descending from the most recent to the oldest to allow us to undo the most recent change first (logical behavior :-)).
      I used execscript() to execute these codes when needed.
      And when undoing is carried on to some point, making a change next to that will delete the undo-redo scripts that was saved after the last undo point we reach (logical too :-))
      And saving data will erase any previous undo-redo scripts (logical conventional approach !)

      I attached a link to a sample form in which you can try changing the values of different controls: textbox with a character value, textbox with a numeric value, editbox, checkbox, image control, spinner, combobox, optiongroup.
      In addition to that, a grid with a source cursor can be updated, new records can be inserted, and records can be deleted.
    All these changes can be undone and redone.

    More Details:

    undoredo Table structure: this table is used to save the undo and redo scripts. It is composed of 3 fields:
    undo_rank, an integer field that saves the rank of the change, and on which the table is indexed with a descending order so that the most recent changes will be undon first.
    un_script and re_script, are memo fields that saves the undo and redo scripts.

    Saving the undo script:

    In this sample I save the undo script temporarily in a custom property in the form. This script will be saved to the undoredo table if a change occur.
    For controls with a text entry (textboxes, editboxes, spinners), the code is saved when the control loses the focus or when the change is committed for other controls and for the cursor in the grid.

    In controls a sample undo script for a control is like this:

    TEXT TO lcUndoScript TEXTMERGE NOSHOW
    <<SYS(1272,this)>>.value='<<this.Value>>'
    <<SYS(1272,this)>>.setfocus
    ENDTEXT


    here the undo script will save the object hierarchy of the control and its value before the change with a setfocus code to that control.
    The resultant code (after textmerging the values) is saved in the undoredo table.

    An undo script of an Insert command will be like this

    TEXT TO lcUndoScript TEXTMERGE NOSHOW
    DELETE FOR it_id=<<lnId>> IN tempGrid
    <<SYS(1272,this)+".parent.grdInvoice.refresh">>
    ENDTEXT


    Saving the redo script:

    The redo script is saved when the changes are committed, like selecting an option, deleting and inserting records, or at lost focus event after changing the new value in text entry controls.

    A sample redo code for that control is like this:

    TEXT TO lcRedoScript TEXTMERGE NOSHOW
    <<SYS(1272,this)>>.value='<<this.Value>>'
    <<SYS(1272,this)>>.setfocus
    ENDTEXT


    A redo script of an Insert command will be like this:

    TEXT TO lcRedoScript TEXTMERGE NOSHOW
    INSERT INTO tempGrid VALUES (<<tempGrid.it_id>>,'<<tempGrid.it_name>>',<<tempGrid.it_quan>>)
    <<SYS(1272,this)+".parent.grdInvoice.refresh">>
    <<SYS(1272,this)+".parent.grdInvoice.clmItem.setfocus">>
    ENDTEXT


    Saving the scripts:

    The scripts are saved so that when moving from record to the next in a descending order, the record contains the undo script with the redo script of the next change. This way when executing undo scripts or redo scripts, the record pointer will move so that the correct next script will be executed in the next undo or redo operation. as it is explained below.

    Executing the scripts:

    In the undo commandbutton click() event I put this code:

    * running the script to undo

    IF !EMPTY(undoredo.un_script)

      TRY
        EXECSCRIPT(undoredo.un_script)
      CATCH
        MESSAGEBOX("error in the current undo script or unavailable resources")
      ENDTRY

      SKIP IN undoredo

      THISFORM.cmdRedo.ENABLED=.T.

      IF EMPTY(undoredo.un_script)
        THIS.ENABLED=.F.
      ENDIF

    ENDIF


    The main piece of code is the EXECSCRIPT() function that will execute the undo script. Then moving to the next record. With enabling or disabling the redo and undo buttons appropriately.

    The redo commandbutton contains a code with a similar idea of the code in the undo commandbutton.

    Save Me button:

    It is not a real save :-)
    It is just to perform like the conventional save functions. Upon saving, the undo - redo data are dismissed to start a new set of undo redo data.

    Notes:

    • There are other minor points that could be found while studying the sample.
    • I don't claim that this is the best way of performing undo - redo operations. I think there are other ways that may be more powerful, more safe or easier. There could be saving of data and parameters in a table instead of scripts, or using arrays, or using parameter classes.
    • The provided sample is to clarify my ideas, it is not intended to be error proof or a perfect design.

    The sample to download:

    http://www.foxite.com/uploads/ccc63abe-5371-4d4b-9ebe-f9c3703eed81.zip

    download the zipped sample, unzip and run the main program (undo-redo.prg) and it will find its way to the sample form, table and the sample pictures :-)

    a snapshot of the sample form:



    Hope it is helpful

    Ammar Hadi ............. Iraq
    July 2009


  • ActiveX keypress() Chaos



    Hi friends …

    PRETEXT
    When I refer to activex controls I mean those which I test well, the richtextctrl, datetime picker and the month view.
    (Other activex controls like forms2 textbox got other issues too, tested partially)



    Activex Keypress(), KeyDown(), KeyUp() Choas
    -------------------------------------------------------------------------

    While I was developing a form in which I decided to use F5 function key as a shortcut key to refresh the form, as usual I set the keypreview property of the form to .T. to trap the key pressed and take an action … I placed a simple code to deal with the F5 key press.

    * The keypress() of the form

    LPARAMETERS nKeyCode, nShiftAltCtrl

    WITH THIS
      DO CASE


       
    CASE nKeyCode=-4  && F5 .. Refresh
          .myRefreshMethod()
         
    NODEFAULT

        CASE nKeyCode= ............
         
    *
          * ... etc
          *


     
    ENDCASE
    ENDWITH


    Till now ok …

    But what I noticed is that when the focus is in a DateTimePicker on the form, the form will not be refreshed when I press F5.

    This weird behavior lead me to test more … I found that:
    •     The keypress of the form was not receiving the key code … i.e. the keypreview setting is useless and the activex disrespect it and never passed the key to the form keypress.
    •     The keypress() of the activex contains this native code:
    *** ActiveX Control Event ***
    LPARAMETERS keyascii


    The twisted thing here is that … the key code passed to the keypress() of the activex is based on the ASCII Character Codes Charts (see below) … which gives some similar key codes to that we depend on (that is listed in the table of key codes in the inkey() section of vfp help) like the number keys codes and basic English letters codes …
    But .. at the same time … keypress() of the activex didn't received the Function keys pressed …. !!   because these are not included in the basic ASCII Character Codes Charts

    The keypress() of the activex doesn't receive the modifier keys (SHIFT, CTRL, ALT) nor does it receive function keys .. but it receive some keys like the Enter key, the Backspace, the Space and the ESC key ..

    And as you noticed there is no nShiftAltCtrl parameter as that in the keypress() of the native vfp controls, so the keypress() of the activex will not reflect the condition of the modifier keys …

    This is because it is ….. ASCII code based … i.e. indicate which ASCII character was passed whatever the key you pressed to invoke it … :-) …
    •     The activex has another two events to receive the key strokes …. :
    KeyDown() and KeyUp() events.  .. let's talk about these …..

    •   KeyDown (as the name is clear) is fired when a keyboard key is pressed down, and KeyUp will fire when it is released to spring up …
    The native code in these 2 events is:

    * the code in the KeyUp and KeyDown events

    *** ActiveX Control Event ***
    LPARAMETERS keycode, shift


    • The irony here is that .. this keycode here could have different values from that of the key codes in the keypress() of the activex and that of the native form …..
    This is because … here, the keycode reflects The Physical Key pressed !!  and there is another table for these key codes .. see below (so you get 3 tables, one for ASCII code, one for these physical key values, and one for the vfp native nkeycode value as shown in the inkey() function help document in vfp help)

    So that keycode of the letter 'a' is the same as to its capital 'A'. The only difference is that shift value will indicate whether modifier keys were pressed or not, the value as follows:

    Shift value                  modifier key

          1                                 SHIFT

          2                                 CTRL

          4                                   ALT

    And this value is additive, that mean if we press SHIFT+CTRL, the value of shift is 3

    But still in fact keyup() and keydown() in this case are not useful to detect which ASCII key was pressed because the value returned by shift is not related to what is the setting of the Caps key is !! …

    In our example, if Caps key was ON and we press the keyboard key 'A' without SHIFT, we are passing 'A' letter to the keyup and keydown events, the keycode value in keyup and keydown will be (65) according to the key codes table used by keyup and keydown, and shift will be 0.

    But Again, if Caps key was OFF and we press the keyboard key 'A' without SHIFT, we are passing 'a' letter to the keyup and keydown events, the keycode value in keyup and keydown will also be (65) according to the key codes table used by keyup and keydown, and shift will also be 0. !! … So, in this case we need to know the condition of the Caps key to know what's going on ..

    On the other hand, when we press the key '0', its key ASCII in the keypress event is (48) whether we pressed the zero in the main keyboard numbers or in the numpad. While in the keyup and keydown, the keycode is (48) if the 0 key pressed in the main keyboard and it is (96) if the numpad 0 was pressed.

    •  Therefore, MSDN .. recommendation is:
    " Although the KeyDown and KeyUp events can apply to most keys, they're most often used for:

    •     Extended character keys such as function keys.
    •     Navigation keys.
    •     Combinations of keys with standard keyboard modifiers.
    •     Distinguishing between the numeric keypad and regular number keys.
    "
    •  Note here that … it is a native behavior of the keyboard to send repeated keys when you keep pressing down the key until you move your finger up. …. Like this if I keep pressing the 'a' key :
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

    In this case .. KeyDown will be fired with each one and KeyUp will be fired at the end only …

    Another (but) … If the key you pressed is processed by keypress() of the activex (if it is an ASCII character), then the keypress() of the activex will be fired also :-) .. and the sequence of events will be like this for one rapid key press:

    KeyDown ---> KeyPress ---> KeyUp

    But for a press with a hold down say for 3 repeats, the event sequence will be like this:

    KeyDown ---> KeyPress --->  KeyDown ---> KeyPress ---> KeyDown ---> KeyPress ---> KeyUp

    A third (but) ….. if the key you are holding is not processed by the keypress (because it is not an ASCII character), like the SHIFT key or the CTRL key, the sequence of events will be:

    KeyDown ---> KeyDown ---> KeyDown ---> KeyUp

    This is important to be noticed if you want to put a code in these events since these will fire repeatedly .. especially the keydown() event that I thought it will fire once as do the keyup() event, but is not

    • If we have a command button with its Default property is set to .T. or a command button with its Cancel Property set to .T., it is supposed that the activex control respects these settings (as the native vfp controls does) but … it doesn't !!
    It doesn't … although in the MSDN documentation of the KeyUp and KeyDown events it says that pressing ENTER key will not invoke these events if there is a commandbutton on the form with its Default property set to .T. … and pressing ESC key will not invoke these events if there is a commandbutton on the form with its Cancel property set to .T. ………

    This is not happening here in visual foxpro .. may be it is true in Visual Basic only !! I don't know.

    • AND .. in MSDN, it is said that TAB key will NOT invoke the keyup and keydown events … but … by testing I found that:
    If we press the TAB key inside the activex .. we have different responses:

    • If there is another control that will receive the focus after the activex, then the event sequence will be as follows:
    KeyDown() of the activex ---> KeyPress() of the parent Form. ---> lostfocus() of the activex !!!

    That's all .. no keypress() of the activex is fired (The ASCII tab chr(9) fires it which can be produced by CTRL+I which inserts a tab space, like in the text part of the richtextctrl, not used to jump from a control to another)  … And .. No keyUp() event.

    • If we used TAB to move from a control on the form to the activex then the sequence of events will be:
    Gotfocus() of the activex ---> KeyUP() … And no KeyDown() event.

    • if there is no other active control that can receive the focus, then the sequence of events will be like this:
    KeyDown() ---> KeyPress() of the Form ---> lostfocus() of the activex ---> gotfocus() of the activex ---> KeyUp()

    • Another nice funny note:
    If we press just a modifier key once, lets take SHIFT, inside the activex … the keypress() will not fire (of course .. not an ASCII character)

    The KeyDown() will be fired first with these values:

    Keycode=16 …. Shift=1

    Then KeyUp() will be fired with these settings:

    Keycode=16 …. Shift=0

    In the keydown event, Its like saying … " you pressed the SHIFT key + SHIFT key"

    And In the keydUp event, Its like saying …" you pressed the SHIFT key without the SHIFT key"

    Funny and ironic :-)

    • So, in foxpro .. activex key events are chaotic … when we look at it with the usual keypress event behavior we used to in vfp controls.

    Now to the struggle to overcome these puzzles ….

    CASE No 1:

    What I was trying to do in my form (as I said above) is that I want to use F5 key to call a custom refresh method in my form ….

    The code I used in the keypress() event of the form is clear … (see it at the beginning of this article). And it works fine inside the form as far as I am away from the datetime picker .. so now how can we overcome that and let the F5 key works even in the activex control. ??

    Several options I tried:

    1) Using ON KEY LABEL:The use of ON KEY LABEL need careful control so that it will not interrupt a critical running code. So, we may use it within the confines of a modal form. Defined in the init event and set to its default in the destroy event like this:

    * init event of the form
    ON KEY LABEL F5 EXECSCRIPT("_vfp.activeform.myRefresh")

    * Destroy event of the form
    ON KEY LABEL F5


    And the code I used in the keypress() event of the form to trap the F5 will be discarded. The code in myRefresh will be excuted directly with the F5 press.

    OR:

    I keep my code in the keypress() of the form and Just use ON KEY LABEL within the confines of the activex itself like this:

    * GotFocus event of the activex
    ON KEY LABEL F5 EXECSCRIPT("_vfp.activeform.myRefresh")

    * LostFocus event of the activex
    ON KEY LABEL F5


    2) Using BINDEVENT():
     
    I was expecting that using BindEvent() will solve the problem .. by trapping the keypress() event of the activex or even the keyUp() or KeyDown() events in a code like this:

    * init event of the form
    BINDEVENT(THISFORM.oleMyActivex, "keypress", thisform,;
     "keypress")


    Here … whether binding keypress, keydown, keyup .. of the activex to the keypress of the form (and whatever the flag is and whatever the type of keycode is) .. IT DOESN"T BIND … It bind it to somewhere?!!

    It looks like the activex is behaving like a standalone object … not respecting KeyPreview nor respecting any BindEvent !! …

    So, I said .. may be I should regard it as a Window .. I tried this code to bind it using hWnd and windows message codes like this:

    * init event of the form
    #define WM_KEYUP   0x0101

    BINDEVENT(thisform.oleMyActivex.hWnd,WM_KEYUP, thisform, "keypress")


    It doesn"t bind too !! :-(

    Notes:

    - The activex hWnd property is not seen in the PEM window but seen with IntelliSense!!

    - I took the windows message value (WM_KEYUP   0x0101) from wmhandler.h available for download in MSDN .. and I uploaded it here (change the extension to .h).

    - I am not sure this last binding is logical or not, but it seems logical to me ..not sure!.

    So, I failed with this method.

    3) Passing the keypress() or keyUp() event key codes and shift values into keypress() of the form like this:

    Use keyUp() to trap the function keys as far as these don't fire the keypress() of the activex.

    * KeyUp() of the activex
    *** ActiveX Control Event ***

    LPARAMETERS keycode, SHIFT

    DO CASE
      CASE keycode=116  && F5
        THISFORM.KEYPRESS(-4,SHIFT)
        NODEFAULT
    ENDCASE


    Here the keycode need to be received as 116 and passed to the keypress() of the form as -4 … !! The tables that help us deal with these differences in keycodes are shown at the end of the article.

    Or we can send the keycode "as-is" and translate it in the form keypress() event.

    CASE No. 2

    If we like to use a richtextctrl control and want to work with it using the conventional shortcuts like: CTRL+B = bold, CTRL+I = italic, CTRL+U = underline. So that just selecting some text in the richtext control and pressing these shortcuts will format it accordingly.

    The code I used to control this shortcut function is:

    * KeyUp() of the richtextctrl
    WITH THIS
    DO CASE
      CASE keycode=66 AND SHIFT=2 && CTRL+B
        .selBold=!.selBold
        NODEFAULT
      CASE keycode=73 AND SHIFT=2 && CTRL+I
        .selitalic=!.selitalic
        NODEFAULT
      CASE keycode=85 AND SHIFT=2 && CTRL+U
        .selUnderline=!.selUnderline
        NODEFAULT
    ENDCASE
    ENDWITH

    I faced some problems here ….

          1) If we run the form that contains the activex within the main vfp window (in-screen or in-top-level form), CTRL+B will call the Breakpoints window.

          2) CTRL+I in the keypress event of the activex is interpreted as a tab space, chr(9) which is the standard ASCII shortcut.

    Progress:

    Running the form As top level form .. solved the problem of Breakpoints window. And the shortcut CTRL+B works fine.

    CTRL+U is already working fine.

    CTRL+I continued to produce tab spaces despite the assignment in the code above and the NODEFAULT command.!!
    Even putting the code in the keydown() event that fires before the keypress didn't change this behavior.

    So, what I did is I cancelled the original assignment of CTRL+I within the activex control and return it back when it loses the focus like this:

    * GotFocus event of the activex
    ON KEY LABEL CTRL+I *

    * LostFocus event of the activex
    ON KEY LABEL CTRL+I


    When I use this approach, the code above that I put in the keyup() event will work fine whether put in the keyup() or keydown() event.

    But CTRL+I will Not work if I used the code in the keypress() event of the activex like this:

    * keyup() of the richtextctrl
    WITH THIS
    DO CASE

      CASE keyascii=2 && ^B
        .selBold=!.selBold
        NODEFAULT

      CASE keyascii=9 && ^I
        .selitalic=!.selitalic
        NODEFAULT

      CASE keyascii=21 && ^U
        .selUnderline=!.selUnderline
        NODEFAULT

    ENDCASE
    ENDWITH

    This is because the keypress() event will not receive CTRL+I anymore since it is no more an ASCII character. But received by keyup() and keydown() as far as they represent a physical key combination as I explained above.

    Note:

    We can Add CTRL+B to the disabling code if we want to run the form in the main vfp window so the code will be:

    * GotFocus event of the activex
    ON KEY LABEL CTRL+I *
    ON KEY LABEL CTRL+B *

    * LostFocus event of the activex
    ON KEY LABEL CTRL+I
    ON KEY LABEL CTRL+B


    Now …. The key code tables ..

    1) nKeyCode values of the native visual foxpro controls are found in vfp help in the INKEY() section.

    2) ASCII character codes chart 1 (0-127) (used in keypress event of the activex): http://msdn.microsoft.com/en-us/library/60ecse8t(VS.71).aspx



    The picture below is to show the description of the codes from 0-31



    3)
    ASCII character codes chart 2 (128-255) (used in keypress event of the activex):http://msdn.microsoft.com/en-us/library/9hxt0028(VS.71).aspx

    "The characters that appear in Windows above 127 depend on the selected typeface."



    4) Key Code Constants for keyup() and keydown():

    http://msdn.microsoft.com/en-us/library/aa243025(VS.60).aspx

    Key Codes

    Constant

    Value

    Description

    vbKeyLButton

    1

    Left mouse button

    vbKeyRButton

    2

    Right mouse button

    vbKeyCancel

    3

    CANCEL key

    vbKeyMButton

    4

    Middle mouse button

    vbKeyBack

    8

    BACKSPACE key

    vbKeyTab

    9

    TAB key

    vbKeyClear

    12

    CLEAR key

    vbKeyReturn

    13

    ENTER key

    vbKeyShift

    16

    SHIFT key

    vbKeyControl

    17

    CTRL key

    vbKeyMenu

    18

    MENU key

    vbKeyPause

    19

    PAUSE key

    vbKeyCapital

    20

    CAPS LOCK key

    vbKeyEscape

    27

    ESC key

    vbKeySpace

    32

    SPACEBAR key

    vbKeyPageUp

    33

    PAGE UP key

    vbKeyPageDown

    34

    PAGE DOWN key

    vbKeyEnd

    35

    END key

    vbKeyHome

    36

    HOME key

    vbKeyLeft

    37

    LEFT ARROW key

    vbKeyUp

    38

    UP ARROW key

    vbKeyRight

    39

    RIGHT ARROW key

    vbKeyDown

    40

    DOWN ARROW key

    vbKeySelect

    41

    SELECT key

    vbKeyPrint

    42

    PRINT SCREEN key

    vbKeyExecute

    43

    EXECUTE key

    vbKeySnapshot

    44

    SNAPSHOT key

    vbKeyInsert

    45

    INS key

    vbKeyDelete

    46

    DEL key

    vbKeyHelp

    47

    HELP key

    vbKeyNumlock

    144

    NUM LOCK key

     

    KeyA Through KeyZ Are the Same as Their ASCII Equivalents: 'A' Through 'Z'

    Constant

    Value

    Description

    vbKeyA

    65

    A key

    vbKeyB

    66

    B key

    vbKeyC

    67

    C key

    vbKeyD

    68

    D key

    vbKeyE

    69

    E key

    vbKeyF

    70

    F key

    vbKeyG

    71

    G key

    vbKeyH

    72

    H key

    vbKeyI

    73

    I key

    vbKeyJ

    74

    J key

    vbKeyK

    75

    K key

    vbKeyL

    76

    L key

    vbKeyM

    77

    M key

    vbKeyN

    78

    N key

    vbKeyO

    79

    O key

    vbKeyP

    80

    P key

    vbKeyQ

    81

    Q key

    vbKeyR

    82

    R key

    vbKeyS

    83

    S key

    vbKeyT

    84

    T key

    vbKeyU

    85

    U key

    vbKeyV

    86

    V key

    vbKeyW

    87

    W key

    vbKeyX

    88

    X key

    vbKeyY

    89

    Y key

    vbKeyZ

    90

    Z key

     

    Key0 Through Key9 Are the Same as Their ASCII Equivalents: '0' Through '9

    Constant

    Value

    Description

    vbKey0

    48

    0 key

    vbKey1

    49

    1 key

    vbKey2

    50

    2 key

    vbKey3

    51

    3 key

    vbKey4

    52

    4 key

    vbKey5

    53

    5 key

    vbKey6

    54

    6 key

    vbKey7

    55

    7 key

    vbKey8

    56

    8 key

    vbKey9

    57

    9 key

     

    Keys on the Numeric Keypad

    Constant

    Value

    Description

    vbKeyNumpad0

    96

    0 key

    vbKeyNumpad1

    97

    1 key

    vbKeyNumpad2

    98

    2 key

    vbKeyNumpad3

    99

    3 key

    vbKeyNumpad4

    100

    4 key

    vbKeyNumpad5

    101

    5 key

    vbKeyNumpad6

    102

    6 key

    vbKeyNumpad7

    103

    7 key

    vbKeyNumpad8

    104

    8 key

    vbKeyNumpad9

    105

    9 key

    vbKeyMultiply

    106

    MULTIPLICATION SIGN (*) key

    vbKeyAdd

    107

    PLUS SIGN (+) key

    vbKeySeparator

    108

    ENTER (keypad) key

    vbKeySubtract

    109

    MINUS SIGN (-) key

    vbKeyDecimal

    110

    DECIMAL POINT(.) key

    vbKeyDivide

    111

    DIVISION SIGN (/) key

     

    Function Keys

    Constant

    Value

    Description

    vbKeyF1

    112

    F1 key

    vbKeyF2

    113

    F2 key

    vbKeyF3

    114

    F3 key

    vbKeyF4

    115

    F4 key

    vbKeyF5

    116

    F5 key

    vbKeyF6

    117

    F6 key

    vbKeyF7

    118

    F7 key

    vbKeyF8

    119

    F8 key

    vbKeyF9

    120

    F9 key

    vbKeyF10

    121

    F10 key

    vbKeyF11

    122

    F11 key

    vbKeyF12

    123

    F12 key

    vbKeyF13

    124

    F13 key

    vbKeyF14

    125

    F14 key

    vbKeyF15

    126

    F15 key

    vbKeyF16

    127

    F16 key



    Download links:

    • A sample form to show the use of shortcuts, print with CTRL-P, format a richtext with CTRL+B, CTRL+I, CTRL+U. here
    • wmhandler.h (windows messaging handler h file): here (change its extension to .h)
    Some resources:


    - Ammar Hadi ................ Iraq


  • Sorting the Grid by Header Clicking Asc / Dec


    In a recent thread about sorting a grid by clicking its headers, I explained a procedure to do that. I think posting it in my blog could be useful for others.

    the thread post is here.

    I improved the code and there is a link below to download a sample form.

    You should have 2 icons in your project, one for descending (a small arrow downward) and the other for ascending (small arrow upward).

    In the init event or at design time, add the appropriate icon to the appropriate header(picture property) so that the correct sorting type on the correct index is reflected when you first show the form.

    The cursor that is the source of the grid should be indexed on all the fields you want to sort on (by using INDEX ON ... ).
    For the code in the header click event to work, it need that when you define indexes start in the same order of columns that the grid start with, so that the first INDEX ON code will create index on the first field column. This is because (it is easy for me) I will use index numbers to deal with sorting. Then you set the order to the index you put the picture in its column header. This way you can Just copy paste the code you put in column header1 click to all other headers and just change the index number in the code to reflect the underlying field/column to sort on.

    the code of the first header click event will be like this:

    ** First column header click event
    SELECT tempStudents
    IF VAL(SYS(21))<>1  && current index number
      SET ORDER TO 1 IN tempStudents
    ELSE
      IF DESCENDING(1,"tempStudents")
        SET ORDER TO 1 IN tempStudents ASCENDING

      ELSE
        SET ORDER TO 1 IN tempStudents DESCENDING

      ENDIF
    ENDIF

    THIS.PARENT.PARENT.SETALL("picture","","HEADER")
    THIS.PICTURE=IIF(DESCENDING(1,"tempStudents"), THISFORM.picdesc, THISFORM.picace) && the sorting icon

    GO TOP IN tempStudents
    THIS.PARENT.PARENT.REFRESH
    THIS.PARENT.SETFOCUS

    In the header of column2, just change the (one)s in the code to 2 and so on for the other headers.

    Download the sample and study it to be more oriented about the idea.

    Here is a picture of the sample form:

    And here is The sample form to download:

    http://www.foxite.com/uploads/5c545b64-b731-4c6f-bae4-4e20ed0c6c5b.rar

    Best Regards

  • The First Visual FoxPro Programmers Group in Iraq


    I would be happy to announce to my friends here in foxite and in Iraq that I am working to accomplish the first Visual FoxPro Programmers Group in Iraq. In fact, I can say it will be the first programmers group in Iraq.

      It was my dream to build this group since about more than 4 years ago, and at that time I send an e-mail to one of the Iraqi Foxers who is a member in foxite too (we never met) and told him that I will work on creating a foxpro group in Iraq. He show his readiness to help me on that. But at that time, I was totally engaged in my Medical study to get the certificate of Neurosurgical Profession. Now it is about a year and a half since I finished my study and training and got the certificate to work as a professional neurosurgeon (with very good marks ;-D ..... ). Now I can say that I got some more time to make my dream come true.

      I was hesitant to announce this group. I was waiting for it to be more stabilized and active to announce it but several e-mails from Boudewijn Lutgerink encouraged me to announce about this group early. Thanks to Boudewijn, his idea is that this early announcement is a good wake up call to Iraqi Foxers (or I can say to Iraqi Programmers in general).

      So, I e-mailed several Iraqi Foxers who are members in foxite and told them about the group. Till now I got a response from 4 members in different cities in Iraq and they was happy about it. I am waiting for the response of others and trying to hunt more (foxes) in the (forest) :-)

      At the time being I got a Google Account for the group in which I build a simple web page, a Discussion Panel, a Blog. Google Account will give us great sharing features with its documents, photos, Calendar, gmail audio and video and text chat and more. I posted these news to my new Iraqi friends to start arranging the work of the group, getting to know each other and build a friendship that will boost our passion to share knowledge and arrange for the first FoxPro Developers Conference ( I hope that be in the near future).

      We are just in the first step ... I hope we can make it a solid reality.

     We wish to hear from all of you foxer friends, about your experience in this regard and your advices to us as a newly born foxpro group.

    The Iraqi Visual FoxPro Programmers Group,

    Google Mail: iraqi.vfppg@gmail.com
    Google Profile
    Site
    Discussion Panel
    Blog

    Ammar Hadi
    June 2009


  • Dynamic Flexible Invoice Report

    Hello my friends ...
    I would like to publish my first weblog post about a method I use to modify the report I use to print invoices.
    In this method I get benefit from 2 of the properties of report objects that is: Comment and User data in the tab of report object properties dialog. I use these properties to assign additional data to the object so that I can find it when I query the source table of the report and that will allow me to specifically change the position, width .. etc that will allow me to generate a dynamic report.
    I uploaded a form here and its associated required table and report to show an example of the method. In this form I designed a grid that will display the data of an invoice in details. The user can choose which column to print and can change the order of columns to fit his/her business needs. With this Idea, I have an application that can be used by different businesses without the need for me to design a special invoice report for each customer. Most of the required data are available and the user can select the way he need his invoice and the way its columns ordered and these settings are saved (in a typical application) and the user can get the invoice printed in the way he wants.
    To be oriented about this technique, download the zipped folder and run the form . Select the columns you need to print and reorder the columns in the way you want and then push the print button, select the folder that you downloaded that contain the needed report and table (rptinvoice.frx, rptinvoice.frt, BasicReport.dbf, BasicReport.cdx). Print the invoice and you will get what I need you to see. change orders and select and deselct again and see the change in the printed invoice.
    Note that I disabled some columns form being deselected. This is just to be more realistic, some columns are essential in the invoice.
    You need to browse the table (basicReport.dbf) and see its structure: You start by designing a complete invoice report and then take the needed data like the left position and width from the source .frx table of the report. The other fields in BasicReport.dbf decide which column is printable and which need to be resized. The main thing in the report, is that I (named) invoice headers, column fields, and the longitudinal separators.
    The example form I posted is simple and not intended for use as is but it will give you the idea on how you can design similar one for your business applications. And there are many aspects and ways to use this idea and the code I used in the form is not the optimum even for me, I just intended to show the idea. I use similar mechanism in one of my applications without the need to customize my reports for clients. They choose what and how the data they need to be printed in their invoices.

    I will be glad to read your questions or suggestions.

    here are some explanatory pictures:


    This is the sample form:



    ...
    Select the folder you downloaded that contains the necessary files:
    From Foxite Weblog

    And here is the printed invoice reflecting the first set of selections in the first image above

    From Foxite Weblog

    And in the following photo, different set of columns selected and the columns re-ordered:
    From Foxite Weblog

    The resulting printed invoice will be like this:
    From Foxite Weblog

    And bellow is a picture for the BasicReport.dbf, notice the fields that are used to control printability, resizability and the order of the named columns. R_width and R_left are the original width and left properties of the report objects that are to be manipulated, taken from the rptinvoice.frx table (fields Width and Hpos, respectively) after primary design, to be as a basic for any furthur calculations needed to reposition and resize the controls.
    From Foxite Weblog

    And bellow is a picture for the report in the designer in its basic primary alignments. What I did is that I assigned (names and types) to the controls  that I want to manipulate. Names are in the user data property and Types in the Comment property of the object:

    From Foxite Weblog

    Below is a picture of the report .frx as a table. Notice the Comment and User fields that I used to store the identifying data of the object
    From Foxite Weblog

    I wish that you study the code I included in the form and let me know your opinions.

    Ammar Hadi ..... Iraq
    ...

This Blog

Post Calendar

<March 2010>
SuMoTuWeThFrSa
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

Post Categories

News

June 2009 - Iraqi Visual FoxPro Programmers Group established.

Syndication