Vous êtes sur la page 1sur 3

Print Article Seite 1 von 3

Issue Date: FoxTalk June 1996


How to Handle Problems with SetFocus()
Gary Jobsey

How do you place the user in a control at a specific time? The Visual
FoxPro SetFocus() method seems like the logical replacement to the
_curobj function in FoxPro 2.x. However, SetFocus() has a few
quirks. Gary Jobsey discusses these oddities and provides some
alternatives.
The SetFocus() method is the Visual FoxPro way of programmatically switching from one control to another. This method is
equivalent to using _CUROBJ and OBJNUM in FoxPro 2.x.

The Visual FoxPro Language Reference Manual's description of SetFocus is brief. It states that the method assigns the
focus to a control, and lists 12 form controls with which the method should work. It goes on to state (negatively) that the
control that is to receive focus won't be assigned focus if its Enabled or Visible properties are set to false (.F.). It also states
that once a control has focus, any user input is directed to that control. The syntax of the method is given as
Control.SetFocus.

Although the syntax shows no parentheses (because the SetFocus method requires no arguments), it's common practice to
include parentheses. This helps to distinguish methods from events. However, you don't have to use parentheses. And they
don't improve or hinder the performance of SetFocus.

So, what traps should you be wary of when using SetFocus? First, FoxPro 2.x developers who are used to including
_CUROBJ in their Valid code will find that they can't do the same with SetFocus.

For SetFocus to successfully assign focus to a target control, the When event of the target control must first fire without (.F.)
being returned. Only then will the GotFocus event of the target control fire. However, if SetFocus is included in the Valid event,
the control won't gain focus, even if the target control fires and returns (.T.).

To demonstrate this, let's take a simple example. (Use the form TEST1.SCX included in the accompanying Download file if
you don't want to create this yourself.) Add three text boxes txtBox1, txtBox2, and txtBox3 to a form and include the following
in the Valid event of txtBox1:

THISFORM.txtBox3.SetFocus()

This is the sequence of events on activation of the Valid event of txtBox1 (such as tabbing out of the control):

txtBox1.Valid (code up to SetFocus instruction)


txtBox3.SetFocus
txtBox3.When
txtBox1.Valid (remaining code in Valid)
txtBox1.LostFocus
txtBox2.When
txtBox2.GotFocus

In this example txtBox2, not txtBox3, gains focus. You can see that the Valid event isn't a good place to insert SetFocus. So
where should it be placed?

The answer is to place SetFocus within the LostFocus event. If in the earlier example SetFocus had been placed in the
LostFocus even instead of in the Valid event, the sequence of events would have looked like this:

txtBox1.Valid
txtBox1.LostFocus
txtBox3.SetFocus
txtBox3.When
txtBox3.GotFocus

Although this sequence has a nice logical order and seems to work in the manner that you would expect from the Language
Reference, practical usage isn't quite so straightforward. Developers often want to include code within the Valid event, but
they run into problems if they do. This can be demonstrated by placing the following in the Valid event of txtBox1:

WAIT WINDOW txtBox1 Valid has fired

Run the form again and press Enter when txtBox1 has focus. You'll see that txtBox3 gains control as before, but notice that

http://foxtalknewsletter.com/ME2/Audiences/Segments/Publications/Print.asp?Module=... 16.02.06
Print Article Seite 2 von 3

txtBox1.Valid fires twice (see TEST2.SCX in the accompanying Download file). This is the sequence of events:

txtBox1.Valid
txtBox1.LostFocus
txtBox3.SetFocus
txtBox3.When
txtBox1.Valid
txtBox3.GotFocus

The logical flow of events has now been destroyed! Why on earth should txtBox1.Valid fire between txtBox3.When and
txtBox3.GotFocus? No doubt Microsoft will fix this problem in the near future.

The use of SetFocus within command buttons can also give rise to strange results. As a further example, include two
command buttons, cmdButton1 and cmdButton2, on the form. Add the Lostfocus code of txtBox1 to cmdButton1 and run the
form. Tab out of cmdButton1, and you'll notice that txtBox3 gains control as expected (see TEST3.SCX in the accompanying
Download file). This is the sequence of events:

cmdButton1.LostFocus
txtBox3.SetFocus
txtBox3.When
txtBox3.GotFocus

If, however, you want cmdButton1 to lose focus as the button is clicked, you could include the following within its click event
(see TEST4.SCX in the accompanying Download file):

THIS.Enabled = .F.

If you run the form again and this time click on cmdButton1, you'll find that cmdButton2 gains control and not txtBox3. This
now the sequence of events:

cmdButton1.Click
cmdButton1.Valid
cmdButton1.LostFocus
txtBox3.SetFocus
txtBox3.When
cmdButton2.When
cmdButton2.GotFocus

It's obvious that SetFocus has to be used with caution. However, you might want to consider alternatives.

One alternative method of switching focus to another control is to return a number from the valid event instead of returning
true or false. A positive number will assign focus forward by that number of controls, using the TabIndex order. A negative
number will move backwards and returning 0 won't change focus (similar to .F.).

Although this approach is easy to use, be careful when adding or deleting controls on a form, as you may have to change the
number returned by the valid event in order to assign focus to the desired place. Less obvious, and perhaps harder to take
into account, is the fact that disabled controls are ignored in the forward / backward control count.

A further alternative, and the one that I prefer, is to use the TabIndex property directly. My solution simply switches the
TabIndex property of the next control in the TabIndex sequence and the control where focus is required. Once the required
control's When event fires, the TabIndex properties are switched back to their original settings.

To utilize TabIndex I've created a new method within my form class that I call "ChangeFocus." The method requires two
parameters, namely the name of the next control and the target control. Using the example with cmdButton1, the following line
could be included within any of the firing events of cmdButton1 and focus would be assigned to txtBox3:

THISFORM.ChangeFocus("cmdButton2","txtBox3")

In addition to the ChangeFocus method, my form class also includes two properties:

lFocus Determines whether TabIndex needs to be reset


aFocus[2,2] Holds details of controls and
TabIndex properties

The When event of all of my control classes include the following code:

IF THISFORM.lFocus
THISFORM.lFocus = .F.
STORE (THISFORM.aFocus[2,1]) TO (THISFORM.aFocus[1,1])
STORE (THISFORM.aFocus[2,2]) TO (THISFORM.aFocus[1,2])
ENDIF

The code for ChangeFocus is as follows:

http://foxtalknewsletter.com/ME2/Audiences/Segments/Publications/Print.asp?Module=... 16.02.06
Print Article Seite 3 von 3

LPARAMETERS cControl1, cControl2


STORE .T. TO ("THISFORM." + cControl2 + ".Enabled")
THISFORM.aFocus[1,1] = "THISFORM." + ALLTRIM(cControl1) + ; ".TabIndex"
THISFORM.aFocus[2,1] = EVALUATE(THISFORM.aFocus[1,1])
THISFORM.aFocus[1,2] = "THISFORM." + ALLTRIM(cControl2) + ; ".TabIndex"
THISFORM.aFocus[2,2] = EVALUATE(THISFORM.aFocus[1,2])
STORE (THISFORM.aFocus[2,1]) TO (THISFORM.aFocus[1,2])
STORE (THISFORM.aFocus[2,2]) TO (THISFORM.aFocus[1,1])
THISFORM.lFocus = .T.

ChangeFocus is provided as a form-level solution to the problem of switching focus between controls. Any controls placed
within any other type of container can't be used as an argument to ChangeFocus. A container object itself can be referenced,
and the first control within that container will gain focus, but other controls within the container can't be accessed using this
method. The reason for this is that TabIndex is a parent container related property, and its use will enable movement only
within the container. Objects placed within a different container will have an independent TabIndex sequence.

The use of CommandGroups is a typical example of what I'm describing. A CommandGroup is a container object, and
Command buttons within it will have a different TabIndex sequence to other controls placed directly within a form.

Of course, ChangeFocus can be modified to provide a more flexible parent reference and allow for another level of container
objects. However, my favored solution is to try and employ SetFocus here or not use CommandGroups.

http://foxtalknewsletter.com/ME2/Audiences/Segments/Publications/Print.asp?Module=... 16.02.06

Vous aimerez peut-être aussi