OnLostFocus Event - Problem with sequence of called functions

Source code related resources

Moderator: Rathinagiri

Post Reply
User avatar
mol
Posts: 3070
Joined: Thu Sep 11, 2008 5:31 am
Location: Myszków, Poland
Has thanked: 180 times
Been thanked: 96 times
Contact:

OnLostFocus Event - Problem with sequence of called functions

Post by mol » Tue Apr 05, 2016 3:01 pm

I need to create form with two textboxes, which values should be checked while focus loose.
I've prepared small sample - two fields, no one can not be empty. Program uses On LostFocus to test content of field. When is empty, message is generated.

Try to leave field 1 empty and observe messages

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'

	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(This.Value)
		MsgStop("Field T1 can not be empty!" + chr(10)+"Focused control is: "+hb_valtoexp(ThisWindow.FocusedControl))
		Form_Main.T1.SetFocus
	endif
 return .t.
 *----------------
function T2_CheckValue
	Form_Main.L_Info.Value := hb_valtoexp(ThisWindow.FocusedControl) + ViewCallStack()
	if empty(This.Value)
		MsgStop("Field T2 can not be empty!" + chr(10)+"Focused control is: "+hb_valtoexp(ThisWindow.FocusedControl))
		Form_Main.T2.SetFocus
	endif
 return .t.
*-------------------
function ViewCallStack
	local i := 2, cStack := ""
	
	while ( !Empty(ProcName(i)) )
		cStack += ProcName(i) + "(" + alltrim(str(ProcLine(i)) )+ ")"+" ----     "
		i++
	end

 return cStack
*------------------

User avatar
mol
Posts: 3070
Joined: Thu Sep 11, 2008 5:31 am
Location: Myszków, Poland
Has thanked: 180 times
Been thanked: 96 times
Contact:

Post by mol » Tue Apr 05, 2016 3:05 pm

OnLostFocus should be fired before the control looses focus, in my opinion.

User avatar
Rathinagiri
Posts: 5250
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Has thanked: 167 times
Been thanked: 165 times
Contact:

Post by Rathinagiri » Tue Apr 05, 2016 3:23 pm

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
*------------------
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.

User avatar
srvet_claudio
Posts: 2044
Joined: Thu Feb 25, 2010 8:43 pm
Location: Uruguay
Has thanked: 35 times
Been thanked: 146 times
Contact:

Post by srvet_claudio » Tue Apr 05, 2016 3:58 pm

Marek,
LostFocus is fired before the window loses the focus, but see the remark section:
https://msdn.microsoft.com/en-us/libra ... .85).aspx
Best regards.
Dr. Claudio Soto
(from Uruguay)
http://srvet.blogspot.com

User avatar
Roberto Lopez
HMG Founder
Posts: 3919
Joined: Wed Jul 30, 2008 6:43 pm
Has thanked: 15 times
Been thanked: 140 times

Post by Roberto Lopez » Tue Apr 05, 2016 4:50 pm

srvet_claudio wrote:Marek,
LostFocus is fired before the window loses the focus, but see the remark section:
https://msdn.microsoft.com/en-us/libra ... .85).aspx
The link is not working... could be you so kind to re-post it?

TIA.
Regards/Saludos,

Roberto


(Veritas Filia Temporis)

User avatar
srvet_claudio
Posts: 2044
Joined: Thu Feb 25, 2010 8:43 pm
Location: Uruguay
Has thanked: 35 times
Been thanked: 146 times
Contact:

Post by srvet_claudio » Tue Apr 05, 2016 5:00 pm

"https://msdn.microsoft.com/en-us/librar ... s.85).aspx"

I'm sorry, is my movil device.
Delete the quotes.
Best regards.
Dr. Claudio Soto
(from Uruguay)
http://srvet.blogspot.com

User avatar
mol
Posts: 3070
Joined: Thu Sep 11, 2008 5:31 am
Location: Myszków, Poland
Has thanked: 180 times
Been thanked: 96 times
Contact:

Post by mol » Tue Apr 05, 2016 6:15 pm

Thanks guys for help!
I've spenthalf a day for searhing right solution. I'll do some twsts tomorrow.
Regards

User avatar
mol
Posts: 3070
Joined: Thu Sep 11, 2008 5:31 am
Location: Myszków, Poland
Has thanked: 180 times
Been thanked: 96 times
Contact:

Post by mol » Wed Apr 06, 2016 7:07 am

Rathinagiri wrote: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!
I fully agree, using this event is very risky...
Rathinagiri wrote: Now, some alternatives:

1. Instead of using Msg functions, you can use a LABEL with Red color to show the error/warning.
Really good alternative, I couldn't find why OnLostFocus is fired (because I've used MsgBox function)...
Rathinagiri wrote: 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.
I need to check value of first field, because the rest of input process depends on this value.
Rathinagiri wrote: 3. You can use a status bar also to indicate the warnings instead of msg functions.
As good as label...
Rathinagiri wrote: 4. Playing a beep is also good sometimes.
It really can help if user has attached speakers and computers aren't located on production hall with loud machines :lol: :lol: :lol:
Rathinagiri wrote:
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.
I thought about it, intermediate state would be useful, as you wrote.
Rathinagiri wrote: To avoid this we can use the wonderful feature:

StopControlEventProcedure ( cControlName, cFormName, lStop )

I am sure now this sample will be right for you.
I dind't know anything about this function, it saved my life :P


Many thanks for help!

User avatar
Roberto Lopez
HMG Founder
Posts: 3919
Joined: Wed Jul 30, 2008 6:43 pm
Has thanked: 15 times
Been thanked: 140 times

Post by Roberto Lopez » Wed Apr 06, 2016 12:30 pm

srvet_claudio wrote:"https://msdn.microsoft.com/en-us/librar ... s.85).aspx"

I'm sorry, is my movil device.
Delete the quotes.
Thanks!
Regards/Saludos,

Roberto


(Veritas Filia Temporis)

Post Reply