You have to add to 1: To stop the native behavior you also have to do a NODEFAULT.
In this problem, it's not that SetFocus doesn't work, it's that the default native behavior of the LostFocus event of any control is to set focus to the next control in tab order, which already has had its When event firing beforehand and respoinded to that with .t. - which is done by default even with no code in When events.
So that's the problem, actually. You can see so with eventtracking, the Setfocus does set focus back to itself, but that doesn't override the behavior of the LostFocus to eventually set focus to the next control. If I do eventtracking the event list lists the when,gotfocus of the textbox with the verification in Lostfocus, then has another gotfocus of that same textbox that is happening because if the SetFocus call, but then also the GotFocus of the next textbox or control in general. Becuase that was already decided by the focus change mechanism before the Lostfocus even got triggered. Learn event sequences. For focus changes you don't just have lostfocus of the previous control and gotfocus of the next control, you first have WHEN of the next control, then lostfocus of previous and gotfocus of next. The documentation of Setfocus tells you it does not work from When, Valid RangeLow and RangeHigh events (the latter of the Spinner control):
SetFocus Method
In Visual FoxPro 9.0, the SetFocus method is not supported in the
When Event,
Valid Event,
RangeHigh Event and
RangeLow Event events. However, you can include a
RETURN Command with an object name (RETURN <ObjectName>) in these events to set the focus to another control. If a RETURN command with an object name is included, the focus is set to the control specified with <ObjectName>.
And the last part of it tells you what else to use; The RETURN mechanism, that's not limited to .T./.F., but also can be an object including THIS and a number, a jump in tab order by N, including 0 to stay.
Back on track of the NODEFAULT/DODEFAULT(): Likewise of only preventing Lostfocus to finally set focus to the next control despite your setfocus, you don't prevent the key press processing by putting code in Keypress onyl ba having code, you only suppress it when also explicitly doing NODEFAULT in your code. DODEFAULT() is not calling the native base behavior that's encoded in the VFP classes within C++ code of the runtime, at least not in every method of every class. The way default behavior of a class works is simply siad "always, unless you do NODEFAULT", so there is no need for DODEFAULT(), usualy. In case of Keypress it works, but when you do it on top of your own code, that would cause all keys to show double. as your code alone doesn't supress it. If you do both DODEFAULT and NODEFAULT you get normal behaviour by explicitly calling it and then supressing the usual run of it as last step. With that you could have This.Value change which is not including the nKeyCode of the keypress event, yet. But then, if you want to react to the value change when it already happened you could use the InteractiveChange event that happens after the Keypress has happened. Agin, learn event sequences.
Anyway, in this case the better solution is to use the Valid event, not the Lostfous. And RETURN 0 or RETURN This, Only use RETURN .F. when you also want the "Invalid Input" wait window or when you have SET NOTIFY OFF, I'd not consider it bad to have it off. I'd rather be cautious with SET SAFETY OFF, though.
Besides all that, the better way of validation is done not at the individual control level, just ignore the existance of the valid event, unless you have a pressing case, like not being able to store an invalid value back into the controlsource of a bound control. Also using buffering then can help to TABLEREVERT and have a mechanism that handles ESC for cancelling. Just as described in
https://hackfox.github.io/section4/s4g117.html