Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Unexpected behavior of nodefault in combination with refresh in grid.

Status
Not open for further replies.

Johnvrmln123

Programmer
Oct 18, 2010
6
NL
Hello to all,

The following code creates a form on a grid. To validate changes in the grid i use the BRCC method an nodefault to ensure that no row change will take place when the record could not be validated.
In this example code the validation should never succeed because it is set to false. (In my main program this would be the outcome of a validation function for instance.)

When another row in the visible part of the grid is selected nodefault prevents de row from being changed. This is ok. But when the grid is scrolled an then the mouse is used to select another row the first visible row in the grid is selected! This is not ok because nodefault should prevent row change. I found that this behavior is caused by the refresh-event that is enterd in the BRCC-event.

Does anyone know why this behavior occurs? and how I can make sure the row is not changed when nodefault is used, without having to offer the refresh?

My code (VFP9, SP2):

Code:
PUBLIC frm
frm = CreateObject ("myForm")
frm.Visible = .T.

DEFINE CLASS myForm As Form
  Width = 400
  Height = 350
  AutoCenter = .T.

  * Add a grid to the form
  Add Object Grid1 as Grid with; 
    RecordSource = "MyCursor", ColumnCount=1, Visible=.t., Top=20, Left=20, Width=360

  * Add cancelbutton to the form
  ADD OBJECT cmd As CommandButton WITH;
    Width=60, Height=30, Left=20, Top=250, Caption="Cancel"

  * Provide validation before row is changed
  PROCEDURE Grid1.BeforeRowColChange
  LPARAMETERS nColIndex
  if inlist(this.RowColChange, 1, 3)  && if row changes
    loValidate = .f.
    if not loValidate  && some validation, in this case always false
      * message ...
      * set the value off some controls
      nodefault  && in this example the user should not be able to change the row in the grid
      thisform.refresh  && to refresh some controls on the form
    endif
  endif

  * Create a release button
  PROCEDURE cmd.Click
  ThisForm.Release
    
  * Create a cursor wiht 100 names
  PROCEDURE Load
  Create Cursor MyCursor (Name Character(20))
  For x = 1 to 100
    Append Blank
    Replace name with "name" + Alltrim(str(x))
  Next x  
  Go Top

ENDDEFINE

Thanks in advance.

John Vermeulen
 
You think too strict of the BeforeRowColChange event. It's grid oriented, not alias oriented. It also wouldn't stop locate or seek to skip to another record without validating input.

It's really the form.refresh() navigating to the new row, by activating the first visible row. Commenting this refresh does leave no ro activated, record pointer remains at position 1.

The solution is to not use row buffering, but table buffering, then it doesn't matter, you can validate in the end when updating all changes. You better go in that direction than trying to fix the active record position.

Also: In table buffering mode you can still commit single rows by Tableupdate(.f.), there is no need to use record buffering. You can move around in the alias and be more relaxed about this, you can put the active record in some property and return to that record, if needed.

Bye, Olaf.
 
PS: Check out eventtracking and coverage to log what's happening.

Bye, Olaf.
 
Thanks Olaf for your reaction.
I see where you are going but table buffering (or table validation) is not an option in my case. It would take too long to explain and this is a really simplified example but it is necessary to validate each row before continuing to the next.

The fact that when no refresh is issued the grid stays the way the user left it (but with the 'nodefault-row' selected outside the visible area of the grid) looks ok to me. But I would expect the grid to show the selected row again when the refresh statement is issued (and not show the first row in the visible part of the grid, that doesn't seem very logical to me).

Is there a way to make the refresh statement work in combination with nodefault? Or another way to show the 'not validated' row again without having to use a work-a-round like 'loRec = recno(), if not validated: go loRec'?

John Vermeulen
(The Netherlands, in case you are questioning my English :))
 
Try this.recordsource = this.recordsource before thisform.refesh(). But don't take that for granted, always working. I warned already.

I still recommend tablebufferin. You can validate recordwise via getnextmodified(), starting with getnextmodifed(0).

Bye, Olaf.
 
Hi John

Just set ScrollBars = 0

...

PROCEDURE Grid1.BeforeRowColChange
LPARAMETERS nColIndex
if inlist(this.RowColChange, 1, 3) && if row changes
loValidate = .f.
if not loValidate && some validation, in this case always false
* message ...
* set the value off some controls
nodefault && in this example the user should not be able to change the row in the grid
THISFORM.GRID1.SCROLLBARS = 0
thisform.refresh && to refresh some controls on the form
ELSE
THISFORM.GRID1.SCROLLBARS = 3
THISFORM.REFRESH()
endif
endif

...

HTH

Mark
 
Hi Mark,

Thanks for replying,
I'm afraid that the damage is already been done when 'THISFORM.GRID1.SCROLLBARS = 0' kicks in. When I ran your code I moved the scrollbar and clicked another row. The first row in the 'viewport' of the grid then gets (wrongly) selected and THEN the scrollbar disappeared. Too late, I'm afraid. And furthermore will 'scrollbars = 0' not prevent the user from using the scrollwheel to scroll down and click another row.

Having said this I think the user should be able to scroll and select another row. But when the row he/she left could not be validated the grid should return to that row instead of selecting the first row in de viewport of the grid. At least, that was the functional thought behind all of this and I hoped that nodefault (whether or not combined with refresh) could provide this.

Did I miss something ...

John

 
Dear John,

No - but I missed something. I tested the code with the arrow keys (it worked) but not with the mouse (it didn't work).

Sorry

Mark
 
Ok Mark, no problem, happpens to me too.

Anyone else any ideas?

John Vermeulen.
 
I already said try this.recordsource = this.recordsource, have you done that? Added in your example code that causes the refresh to scroll back to the record 1.

Bye, Olaf.
 
Sorry Olaf, i was away for a few days. Will try soon and let you know.
John
 
Hi Olaf,
In the example your code works fine! Thanks Olaf.
In my app however it doesn't. Although the BRCC code is the same. I have to find out why.
Can you tell me how you came to this solution and why it works?

John
 
Resetting the recordsource is a hack to reset certain things, eg also the position the grid should show, so a refresh repaints the grid from the current record position and the pointer therefor also stays at that record instead of moving to the first visible record.

It could fail if the recordsource is not an alias recordsourcetype. It could fail on things I can't imagine or remember right now, I also don't use this hack. That's why I also said caution with this.

If you add code to store RECNO() in the BeforeRowColChange() and check whther you are at a different record in AfterRowColChange() or Valid() of the grid. You still can mimmick record buffering in table buffering mode, as you then can go back to the record which was left, validate and Tableupdate() that single record or refresh the grid positioned on that record. So I stay with the original recommendation: Convert to table buffering.

Bye, Olaf.
 
Hi John,

Just leave out "ThisForm.refresh()" - see modified code below

***** SoC *****

PUBLIC frm
frm = CreateObject ("myForm")
frm.Visible = .T.
frm.Show
READ Events
CLOSE ALL
CLEAR ALL


DEFINE CLASS myForm As Form
Width = 400
Height = 350
AutoCenter = .T.

* Add a grid to the form
Add Object Grid1 as Grid with;
RecordSource = "MyCursor", ColumnCount=1, Visible=.t., Top=20, Left=20, Width=360

* Add cancelbutton to the form
ADD OBJECT cmd As CommandButton WITH;
Width=60, Height=30, Left=20, Top=250, Caption="Cancel"

* Provide validation before row is changed
PROCEDURE Grid1.BeforeRowColChange
LPARAMETERS nColIndex
LOCAL liValidate

liValidate = 0
if liValidate = 0 && some validation, in this case always false
* message ...
* set the value off some controls
Nodefault && in this example the user should not be able to change the row in the grid
ELSE
This.refresh && to refresh some controls on the form

endif

ENDPROC

PROCEDURE cmd.Click
CLEAR Events
ThisForm.Release
ENDPROC

PROCEDURE Destroy()
CLEAR Events
ThisForm.Release
ENDPROC

PROCEDURE Load
Create Cursor MyCursor (Name Character(20))
For x = 1 to 100
Append Blank
Replace name with "name" + Alltrim(str(x))
Next x

Go Top
ENDPROC

ENDDEFINE

***** EoC *****

Besides be aware of (from Hackers Guide to Visual Foxpro 7)

RowColChange contains a number describing what changed, whether it was the row, column, both, or neither. It defaults to zero when the grid is initialized, and after a GRID REFRESH.

hth

Mark
 
Hi John,
A fully working example


PUBLIC frm
frm = CreateObject ("myForm")
frm.Visible = .T.
frm.Show
READ Events
CLOSE ALL
CLEAR ALL


DEFINE CLASS myForm As Form
Width = 400
Height = 350
AutoCenter = .T.

* Add a grid to the form
Add Object Grid1 as Grid with;
RecordSource = "MyCursor", ColumnCount=1, Visible=.t., Top=20, Left=20, Width=360

* Add cancelbutton to the form
ADD OBJECT cmd As CommandButton WITH;
Width=60, Height=30, Left=20, Top=250, Caption="Cancel"

* Provide validation before row is changed
PROCEDURE Grid1.BeforeRowColChange
LPARAMETERS nColIndex
LOCAL liValidate

IF LEN(ALLTRIM(_screen.ActiveForm.ActiveControl.Value)) > 6
liValidate = 0
if liValidate = 0 && some validation, in this case always false
WAIT WINDOW + " Wrong Value " Nowait
* message ...
* set the value off some controls
Nodefault && in this example the user should not be able to change the row in the grid
ELSE
This.refresh && to refresh some controls on the form

ENDIF
ENDIF

ENDPROC

PROCEDURE cmd.Click
CLEAR Events
ThisForm.Release
ENDPROC

PROCEDURE Destroy()
CLEAR Events
ThisForm.Release
ENDPROC

PROCEDURE Load
Create Cursor MyCursor (Name Character(20))
For x = 1 to 100
Append Blank
Replace name with "name" + Alltrim(str(x))
Next x

Go Top
ENDPROC

ENDDEFINE


hth

Mark
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top