||Re: Grid Navigation
Mervyn Bick <email@example.com>
||Mon, 12 Feb 2018 11:06:27 +0200
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
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
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.
drop table test_grid_callback
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))
for n = 1 to 10
replace job_no with '1'+str(N,3,0,'0')
replace customer with 'Abel'
replace customer with 'Baker'
replace customer with 'Charlie'
replace customer with 'Delta'
replace customer with 'Echo'
** END HEADER -- do not remove this line
// Generated on 2018/02/12
f = new test_rid_callback2Form()
f.mdi = false // ensure not MDI
class test_rid_callback2Form of FORM
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
this.TEST_GRID_CALLBACK1 = new QUERY(this)
left = 2.0
top = 1.0
width = 14.0
height = 1.0
sql = 'select * from "test_grid_callback.DBF"'
active = true
readOnly = true
readOnly = true
readOnly = true
this.GRID1 = new GRID(this)
onLeftMouseUp = class::GRID1_ONLEFTMOUSEUP
dataLink = form.test_grid_callback1.rowset
height = 15.5
left = 12.0
top = 6.0
width = 103.0
this.rowset = this.test_grid_callback1.rowset
form.test_grid_callback1.active = false
if type("dBGetCurrentThreadId") # "FP"
extern CULONG dBGetCurrentThreadId() kernel32 from "GetCurrentThreadId"
if type("dBGetWindowLong") # "FP"
extern CLONG dBGetWindowLong(CHANDLE, CINT) user32 from "GetWindowLongW"
if type("dBSetWindowsHookEx") # "FP"
extern CLONG dBSetWindowsHookEx(CINT, CPTR, CHANDLE, CULONG) user32 from "SetWindowsHookExW"
if type("dBUnhookWindowsHookEx") # "FP"
extern CLOGICAL dBUnhookWindowsHookEx(CHANDLE) user32 from "UnhookWindowsHookEx"
if type("dBCallNextHookEx") # "FP"
extern CLONG dBCallNextHookEx(CHANDLE, CINT, CUINT, CUINT) user32 from "CallNextHookEx"
// if type("GetAsyncKeyState") # "FP"
// extern CSHORT GetAsyncKeyState(CSHORT) user32
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())
function GRID1_onLeftMouseUP(flags, col, row)
//detach the hook.
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
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