Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Changing controls with setfocus()
Message
From
25/08/2008 00:51:49
 
 
To
24/08/2008 15:12:57
General information
Forum:
Visual FoxPro
Category:
Forms & Form designer
Environment versions
Visual FoxPro:
VFP 9 SP2
OS:
Vista
Database:
Visual FoxPro
Miscellaneous
Thread ID:
01341355
Message ID:
01341438
Views:
15
>>>On my Form, I have several txtBox controls that each (on lostfocus), update the RecordSource of a single grid on the form and refresh the grid, as the ordered row order has likely changed with the txtBox change. This works fine. I have now written some code that positions the grid’s ActiveRow appropriately (mainly so the rows above ActiveRow don’t hide above the top visible row with the ActiveRow on the top row). This works fine too.
>>>
>>>The problem is that to read the grid’s activerow position (needed to reposition the grid row content), the grid must have focus (I do this successfully in the txtBox lostfocus just before calling the grid refresh); but following the grid.refresh, I want to put the focus either back on the calling control, keypress a TAB and have it move without triggering the lostfocus event (again), or alternately put the focus back on the next control (which I was thinking of tabbing to) directly after finishing the grid’s refresh.
>>>
>>>I started coding this, but it’s getting very very messy, and I suspect that there might be a different approach that is cleaner. Any thoughts?
>>
>>One approach would be to create a method in the form that contains all the objects in question - textboxes, grid etc. e.g. MyForm.MyCustomMethod()
>>
>>This method would then be called from each of your Textboxes as required.
>>
>>One useful trick would be to pass a reference to the calling control to .MyCustomMethod. That way, the method knows where to return focus when it's done i.e.
>>
>>* MyTextBox.SomeMethod()
>>
>>=ThisForm.MyCustomMethod( param1, param2, ..., This )
>>
>>* MyForm.MyCustomMethod
>>
>>LPARAMETERS ;
>>  param1 ;
>>  , param2 ;
>>  , ... ;
>>  , loCallingObject
>>
>>* Do some stuff ...
>>* Then ...
>>loCallingObject.SetFocus()
>>
>
>Thanks Al
>Your suggested approach is quite similar to what I had started trying. The key from your post was that there is no magic bullet solution, so you got me back to work on the problem. It’s working now, and here’s what I ended up with:
>
>A global (public) variable gcCameFrom
>
>For each txtBox, or other control, that changed the grid’s RecordSource content
>In GotFocus()
gcCameFrom = This.Name
>dodefault()
>In LostFocus()
dodefault()
>thisform.MyGridUpdate()
>In thisform.MyGridUpdate()
with thisform
>  * any form specific recalc or refresh code here, followed by:
>  .grdTheGrid.setfocus()
>  .grdTheGrid.refresh()   && holds the grid's row positioning code
>  &gcCameFrom..SetFocus()
>endwith
>In grdTheGrid.refresh()
dodefault()
>* Followed by the grid's row positioning code
>This approach allows a return to the calling control from the grid so that I still move to the next TabOrder control if I’m leaving the control with a Tab key.

I'd be inclined to create and use a form property instead of a global variable. That way you don't run into problems if you have multiple instances of your form open at once.

Also, as I believe Dragan is suggesting, I'd store a direct object reference ("This") to the property or memvar, avoiding the name issues Dragan points out, as well as removing the awkward macro expansion.

Yet another thing you might want to consider would be to put all of the non-grid controls into a Container, and put various methods in that container rather than in the Form. It helps encapsulate control behaviour closer to the controls themselves. You could create and initialize a custom property of the container to hold a reference to the grid in question; potentially your code would not have to access Form methods at all.
* Call a Container Method
=This.Parent.SomeMethod()

* Container.Init()
This.oCallingObject = NULL
This.oGridReference = ThisForm.grdTheGrid

* Reference the grid from a textbox:
This.Parent.oGridReference.SomeMethod/SomeProperty

* Set the Calling Object reference in the Container:
This.Parent.oCallingObject = This

etc. etc.
SET PROSELYTIZING ON

My personal preference in this sort of situation is to try hard to not store object references to global variables or (other) object properties. I tend to pass references to This directly to methods. If the called method calls another that also requires the object reference, it duly passes it along. Avoiding storing object references can reduce some hard-to-debug issues that Dragan points out, involving dangling object references.

I find this makes the creation of "generic" code easier and often encapsulates behaviour better, as you don't have to semi-hard code calls or references to public variables or ThisForm properties. Interfaces can be simpler, improving class design and making code more reusable.

Another general thought is that BINDEVENT() can sometimes help in this type of situation, although its use can mean a lot of class and code refactoring.

Of course, nothing beats working code, if you've got that already ;)

SET PROSELYTIZING OFF
Regards. Al

"Violence is the last refuge of the incompetent." -- Isaac Asimov
"Never let your sense of morals prevent you from doing what is right." -- Isaac Asimov

Neither a despot, nor a doormat, be

Every app wants to be a database app when it grows up
Previous
Next
Reply
Map
View

Click here to load this message in the networking platform