Subject Re: Grid Navigation
From Mustansir Ghor <mustan31@hotmail.com>
Date Mon, 12 Feb 2018 14:23:56 -0500
Newsgroups dbase.getting-started

Dear Mervyn

Thank you for the solution but using CALLBACK with zero on knowledge on windows operating I am reluctant to use it. Perhaphs you can suggest me a literature to learn little on  winuser.h file, user32, kernel32 functions.

But as you see suggested I see the standard norm on a form  is to to use entryfield/combobox for adding and editing items that are being displayed in the grid.

Consider the case where you were to add item and its qty as new item, its unit of sales and price being picked fom lookuprowset and amount to be calculated.

In the above scenario, we can select item from combobox, enter qty in the entryfield, then select pushbutton add to add the new item into grid and bring focus back to item combobox. However how then we handle correction of item, or removal of item. I am still confused how user will react to these objects, will they use mouse or keyboard

I understand this is truly on the designer problem but little guidance on this will  help go to with standard data entry forms. Unfortunately my item selection does not use bar code scanner

Best  Regards
Mustansir



Mervyn Bick Wrote:

> On 2018-02-10 10:28 PM, Mustansir Ghor wrote:
> > Dear All
> >
> > I have grid with 5 columns.  1st column is combobox, 2nd column is entryfield, 3rd and 4th are reference display from lookuprowset. 5th is a calculated one based on item selection from 1st column and entryfiled  of 2nd column. I therefore wish to restrict navigation to only 2 columns 1st and 2nd. Can anybody advise how this can be achieved.
>
> The fact that you have a combobox in your grid suggests that you are
> using the grid for data entry and/or editing.
>
> Over the years I've come to the conclusion that using a grid for editing
> is not a good idea.  It is simply too easy to make mistakes.  I prefer
> to display data in a grid and use entryfields for adding/editing fields
> in conjunction with Edit, Add, Save and Abandon buttons.  Still, if you
> really want to use a grid for adding/editing rows it can be done.
>
> The only way I could restrict navigation to the first two columns of the
> grid was to use the CALLBACK command.  This means this will only work
> for dBASE Plus 8 and later.  (This is where someone, usually Ivar
> Jesson, pops up with a Better Way than the Bick Way. :-) )
>
> The attached example installs a keyboard "hook".  This intercepts all
> keystrokes to the form by forcing Windows to execute a function in the
> program in place of it's own function.  The program can then monitor all
> keystrokes including those that the normal onKey and Key event handlers
> miss.
>
> Normally the keystroke is passed straight back to the Windows function
> but by returning 1 instead of calling the Windows hook the keystroke is
> ignored by Windows.  If the keystroke was Tab or Enter in the second
> column the function doOnEnter is executed and the Tab or Enter keystroke
> is not returned to Windows but is dealt with by doOnEnter..
>
> If the user uses the mouse to click in any of the fields column 1 gets
> focus.
>
> Some points to watch
>
> The underlying fields for columns 3 - 5 must have their readonly
> property set true.
>
> After the form has been run I've found that the sourcecode editor
> crashes after changes have been made to the code.  The changes are,
> however, saved first.  Close dBASE and restart.  It's a PITA but nothing
> else seems to get hurt.  I'll be submitting a bug report later.
>
> Mervyn.
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>       clear
> if file('test_grid_callback.dbf')
>     drop table test_grid_callback
> endif
>
> if not file('test_grid_callback.dbf')
>    create table test_grid_callback (job_no character(5),customer character(15),address1 character(15),address2 character(15),address3 character(15))
>      
>    use test_grid_callback
>    generate 10
>    go top
>    for n = 1 to 10
>        replace job_no with '1'+str(N,3,0,'0')  
>        SKIP
>    next
>    go top
>    replace customer  with 'Abel'
>    SKIP
>    replace customer  with 'Baker'
>    SKIP
>    replace customer  with 'Charlie'
>    SKIP
>    replace customer  with 'Delta'
>    SKIP
>    replace customer  with 'Echo'
>    SKIP
>    use
> endif
>
> ** END HEADER -- do not remove this line
> //
> // Generated on 2018/02/12
> //
> parameter bModal
> local f
> f = new test_rid_callback2Form()
> if (bModal)
>    f.mdi = false // ensure not MDI
>    f.readModal()
> else
>    f.open()
> endif
>
> class test_rid_callback2Form of FORM
>    with (this)
>       canClose = class::FORM_CANCLOSE
>       onOpen = class::FORM_ONOPEN
>       onClose = class::FORM_ONCLOSE
>       height = 26.7273
>       left = 44.2857
>       top = 3.2273
>       width = 134.8571
>       text = ""
>       scrollBar = 2        // Auto
>    endwith
>
>    this.TEST_GRID_CALLBACK1 = new QUERY(this)
>    with (this.TEST_GRID_CALLBACK1)
>       left = 2.0
>       top = 1.0
>       width = 14.0
>       height = 1.0
>       sql = 'select * from "test_grid_callback.DBF"'
>       active = true
>    endwith
>
>    with (this.TEST_GRID_CALLBACK1.rowset)
>       with (fields["address1"])
>          readOnly = true
>       endwith
>       with (fields["address2"])
>          readOnly = true
>       endwith
>       with (fields["address3"])
>          readOnly = true
>       endwith
>    endwith
>    
>
>    this.GRID1 = new GRID(this)
>    with (this.GRID1)
>       onLeftMouseUp = class::GRID1_ONLEFTMOUSEUP
>       dataLink = form.test_grid_callback1.rowset
>       height = 15.5
>       left = 12.0
>       top = 6.0
>       width = 103.0
>    endwith
>
>  
>    this.rowset = this.test_grid_callback1.rowset
>
>    function form_onClose()
>       form.test_grid_callback1.active = false
>       return
>
>    function form_onOpen()
>    clear
>       #include winuser.h
>       if type("dBGetCurrentThreadId") # "FP"
>          extern CULONG dBGetCurrentThreadId() kernel32 from "GetCurrentThreadId"
>       endif
>       if type("dBGetWindowLong") # "FP"
>          extern CLONG dBGetWindowLong(CHANDLE, CINT) user32 from "GetWindowLongW"
>       endif
>       if type("dBSetWindowsHookEx") # "FP"
>          extern CLONG dBSetWindowsHookEx(CINT, CPTR, CHANDLE, CULONG) user32 from "SetWindowsHookExW"
>       endif
>       if type("dBUnhookWindowsHookEx") # "FP"
>          extern CLOGICAL dBUnhookWindowsHookEx(CHANDLE) user32 from "UnhookWindowsHookEx"
>       endif
>       if type("dBCallNextHookEx") # "FP"
>          extern CLONG dBCallNextHookEx(CHANDLE, CINT, CUINT, CUINT) user32 from "CallNextHookEx"
>       endif
> //      if type("GetAsyncKeyState") # "FP"
> //         extern CSHORT GetAsyncKeyState(CSHORT)  user32
> //      endif
>                 CALLBACK CLONG hookWndProc(CINT, CUINT, CUINT) OBJECT this
>       this.hookProc = GetCallAddress(class::HOOKWNDPROC)
>       this.hInst = dBGetWindowLong(this.hwnd, GWL_HINSTANCE)
>       this.hook = dBSetWindowsHookEx(WH_KEYBOARD, this.hookProc, this.hInst, dBGetCurrentThreadId())
>       form.grid1.setFocus()
>       return
>         
>
>    function GRID1_onLeftMouseUP(flags, col, row)
>       keyboard '{tab}'
>       keyboard '{home}'
>       return
>
>    function form_canClose
>       //detach the hook.
>       dBUnhookWindowsHookEx(this.hook)
>       return true
>
>
>    function doOnEnter
>       if form.test_grid_callback1.rowset.atlast()
>          form.test_grid_callback1.rowset.beginAppend()
>       else  
>          form.test_grid_callback1.rowset.next()
>       endif  
>       keyboard '{home}'
>       return
>      
>
>    function hookWndproc(hCode, wParam, lParam)
>       cReturn = dBCallNextHookEx(this.hook, hCode, wParam, lParam)
>       if (wParam = VK_RETURN or wParam = VK_TAB) and hcode = 0 and not bitset(lparam,31)
>          if upper(form.activeControl.name) = 'GRID1'  and form.grid1.currentcolumn = 2
>             //tab or return in column 1 is ignored here and passed on to Windows like any other keystroke
>             if form.test_grid_callback1.rowset.state = 3 and form.grid1.currentcolumn = 2
>                form.test_grid_callback1.rowset.save()
>              endif  
>             class::doOnEnter() //Deal with tab or return in coulmn 2
>             cReturn = 1 // Don't call next hook. This prevents Enter or Tab from moving cursor to next field
>          endif
>       endif
>       return cReturn
>  
>
> endclass
>