Marek,
When using LostFocus property of two controls (that too consecutive) we should be very careful.
If you use Msg functions in between, then the head ache is still more!
The problem becomes still complex when we again set focus to the original control.
Using 'this.value' inside lostfocus event is also a problematic (might be a bug?) As you can see, before a lostfocus event of first control is fired, the next control is gotfocus. When you are again making a set focus to the original control, the second control lostfocus is fired!
Invoking the Msg function is nothing but activating a modal window created over and above our application window. So, this naturally fires the LostFocus event of the textbox again!
Now, some alternatives:
1. Instead of using Msg functions, you can use a LABEL with Red color to show the error/warning.
2. Data can be validated at the time of saving the record (may be after pressing the SAVE button. This is what I do) This way, I allow the user to move on through all the controls without any restrictions.
3. You can use a status bar also to indicate the warnings instead of msg functions.
4. Playing a beep is also good sometimes.
mol wrote:OnLostFocus should be fired before the control looses focus, in my opinion.
You can not fire lostfocus event before losing the focus as we can't know when it will. So, it is rightly named 'LOST' focus.
IMHO, there is no such intermediate state between controls. When one control loses focus, automatically the next control in the tab order gets the focus and there is no intermediate state in between. When we manually set focus to the original control the lostfocus of the second control is fired.
To avoid this we can use the wonderful feature:
StopControlEventProcedure ( cControlName, cFormName, lStop )
I am sure now this sample will be right for you.
Code: Select all
#include "hmg.ch"
Function Main
DEFINE WINDOW Form_Main ;
AT 0,0 ;
WIDTH 640 HEIGHT 600 ;
TITLE 'ON LOST FOCUS TEST' ;
MAIN
@ 100,10 LABEL L1 ;
WIDTH 150 HEIGHT 20 ;
VALUE 'FIELD 1'
@ 100,100 textbox T1 ;
WIDTH 150 HEIGHT 20 ;
VALUE '' ;
On lostfocus T1_CheckValue()
@ 130,10 LABEL L2 ;
WIDTH 150 HEIGHT 20 ;
VALUE 'FIELD 2'
@ 130,100 textbox T2 ;
WIDTH 150 HEIGHT 20 ;
VALUE '';
On lostfocus T2_CheckValue()
@ 180,10 LABEL L_Info1 ;
WIDTH 120 HEIGHT 18 ;
VALUE 'Focused control'
@ 200,10 LABEL L_Info ;
WIDTH 600 HEIGHT 380 ;
BACKCOLOR {255,255,0};
FONTCOLOR {255,0,0};
VALUE 'Info label'
@ 10, 10 label warning width 200 fontcolor { 255, 0, 0 }
END WINDOW
CENTER WINDOW Form_Main
ON KEY ESCAPE OF FORM_MAIN ACTION FORM_MAIN.RELEASE
ACTIVATE WINDOW Form_Main
Return
*----------------
function T1_CheckValue
Form_Main.L_Info.Value := hb_valtoexp(ThisWindow.FocusedControl) + ViewCallStack()
if empty(Form_Main.t1.Value)
form_main.warning.value := 'Field T1 can not be empty!'
&& MsgStop("Field T1 can not be empty!" + chr(10)+"Focused control is: "+hb_valtoexp(ThisWindow.FocusedControl))
StopControlEventProcedure ( 't2', 'Form_Main', .t. )
Form_Main.T1.SetFocus
StopControlEventProcedure ( 't2', 'Form_Main', .f. )
else
form_main.warning.value := ''
endif
return .t.
*----------------
function T2_CheckValue
Form_Main.L_Info.Value := hb_valtoexp(ThisWindow.FocusedControl) + ViewCallStack()
if empty(Form_Main.t2.Value)
form_main.warning.value := 'Field T2 can not be empty!'
&& MsgStop("Field T2 can not be empty!" + chr(10)+"Focused control is: "+hb_valtoexp(ThisWindow.FocusedControl))
StopControlEventProcedure ( 't1', 'Form_Main', .t. )
Form_Main.T2.SetFocus
StopControlEventProcedure ( 't1', 'Form_Main', .f. )
else
form_main.warning.value := ''
endif
return .t.
*-------------------
function ViewCallStack
local i := 2, cStack := ""
while ( !Empty(ProcName(i)) )
cStack += ProcName(i) + "(" + alltrim(str(ProcLine(i)) )+ ")"+" ---- "
i++
end
return cStack
*------------------