Event Sequence

Hi. I’m having some trouble understanding what I’m seeing in an event sequence between a Textfield and a Button:

I have a Textfield (TF) that calls a method that updates a variety of other fields on a window. It calls that method from the LostFocus event.
I have a button (SAVE) that runs some code in its Action event (I have also put it in MouseDown and MouseUp with the same results). At the beginning of this code it checks to make sure that the appropriate fields have been filled in, a process which is taken care of by the method called by TF’s LostFocus event.

When I tab from TF to any other control and then press the Save button, the validation works fine. Lost focus runs the method, the other fields are updated and all is well when I press SAVE.

When the TF is active, the appropriate data is entered (same data as when pressing TAB) and I press SAVE directly without first using TAB, the validation error is thrown. So after looking into this, it appeared that the work in the method was not completed by the time the data was evaluated in the Save button’s event. I then added a time stamp to the bottom of the method called in the Lostfocus event and the top of the Save button’s Action event. The time stamp simply stored date.totalseconds

Method (from LostFocus): 3479457809.7080002
Save button (from Action): 3479457809.704

So, unless I’m misreading the above, the Button Action event was called before the LostFocus event (and therefoer the method) had completed executing.

I can think of plenty of ways to work around what I’m seeing, so this question isn’t really about what to do. What I’m wondering is if this is NORMAL, expected behavior. Any thoughts?

I’m using RS 2001 R3 Pro.

Beyond some very basics like the Window open event will be raised after all controls on the page open events you should not rely on or code expecting a certain order
Doing so will give you surprises when/if you move code from one platform to another

But using 2001 ? You sure about that ?

Oops- that is a typo. Should be 2011!

Thanks.

The actions in one event can/could cause other events to occur.
I suspect that may be what you’re seeing.

What platform are you on ?
That’s probably relevant because focus handling IS different between OS X & Windows/Linux.
On Windows & linux buttons can & do get focus where as on OS X it may not.

This is one area where the platform differences are important and why depending on event order can be problematic.

I’m on Windows.

This has me thoroughly confused. I’m just surprised that you cannot rely on the LostFocus event of 1 object in the very next object’s events. It seems like that would be on of those you mentioned as being very basic.

I have run into unexpected event firings in the past. They make sense to me, even if not always intuitive. But without any way in the code for that field to get focus, I cannot understand how it can LoseFocus a second time.

To make absolutely certain of the order of events, I’m going to add a counter variable by the LostFocus time stamp to make sure that I’m not executing it twice and merely displaying the latter time stamp.

Clicking a button doesn’t necessarily give it focus
However, on Windows you can tab to a button and that will move focus
Not so on OS X

What we often recommend to people is using subclasses of controls so you can add something like a “Don’t run the event if the change is caused by me doing something programmatic” - i.e. setting the value in code, moving focus in code etc.

That way you can do things differently if it’s a change from code vs something in the UI the user triggered by tabbing, pressing a button etc

Ya, I’m doing this without a subclass of the control for different things. When I wrote the bit about LostFocus vs GetFocus, I’ve used the button’s action event, mousedown event and mouseup event, all the the same results. I see what you’re saying, but I’m no less shocked :slight_smile:

Heres what I set up
Something simple - 2 buttons and 2 text fields on a window
In both the buttons got focus, lost focus, and action events I simply log the name of the event
In both textfields got focus, lostfocus, and textchange events again I log the event name
The I run this with DebugView running as well (to watch the events)
In 2014r1 on Windows I see
[1596] Window1.PushButton1.GotFocus <<<<< app starts with this
[1596] Window1.PushButton1.Action <<<<<< I pressed the button
[1596] Window1.Untitled <<<<<<<<< my validation method runs
[1596] Window1.TextField2.TextChange <<<< the validation method inserts data to textfield2
[1596] Window1.PushButton1.LostFocus <<<<<<<<<<<<<<< I move to DebugView to copy the event sequence

Things seem in order enough here but DebugView can certainly help with some logging in your app

What you describe is “normal” although not necessarily what you would expect. Bottom line, you cannot rely on LostFocus for field validation. I subclass all my controls and give each one a “dirty” flag, meaning it has been changed but not yet validated. In the “submit” sequence for the form, I run through all the controls and validate any that are dirty. Then I trigger an event on the window (again, a subclass) that does an overall validation - do the values make sense when taken altogether. Often, an individual control may be valid at the time it was set, but subsequent changes make its value invalid. (Eg., the zip code might be valid, until you change the country, then it is no longer valid, so one last sanity check is useful.)

Note, I DO validate on LostFocus (you want to get any error messages as close to the event as possible), but I don’t rely on that alone.

Thanks, all. I figured out what was happening. LostFocus event was NOT firing at all when I clicked the button. My errormessage is what was gaining focus, causing my textfield to lose it, and that was why it appeared to be triggering in the sequence it was triggering in. So, in a round about way, I’ve done what others have suggested and made my verification method more generic. Now it validates the entire form on Save button, along with individual fields as possible through the lost focus event.

As always- Thanks for the help!

Is there available a chart or table that shows what events trigger other events? I’m having a lot of problems my program - I think it is going into an infinite look because of events triggering other events and it would be easy to diagnose the problem with the help of such a chart or table.

No, and it varies between Windows / Mac

Dont rely on an event to have done something before you click a button to save or close a dialog.

Put all the ‘bring things up to date’ code into a method.

Have the textfield call the method by all means, but call it again as the first line of the button code.(so you know everything is up to date)
If you dont want the textfield to do it again on lostfocus, make the button disable the controls before it does its business.
And have the textfield only call the update when it is enabled.

no such table, because thats not how it works… EVENTS are just that, a mouse movement is an event, a mouseclick is an event, a keystroke is an event and those are just some of the EXTERNAL events that occur. Internally there are dozen (hundreds) more, the data in a control changed, a window moved, opened, closed.

Is there a direct dependecy between Event X and Event Y to the point a spreadsheet of relationships could be accuratly created? Not so much.

The biggest event related issue that causes an infinite loop or recursion, is when an event ultimately calls itself, without ever unwinding the calling path …

Thanks, Jeff and Dave! Knowing some general rules for program architecture is especially valuable. I guess I’m going to move some code. per your suggestions.

If that doesn’t work I’ll have to bite the bullet and set breakpoints in all possible methods and event handlers and track what triggers what that way: run the program, see what breakpoint is triggered, remove that breakpoint, run again, etc. A lot of work, which is why those general rules are so valuable to me.

Opening is usually the most tricky thing to do with events. If you have a complicated sequence the following may help:

  • Do your initialisation in one method. Don’t have each control initialise itself.

  • Add a property “isOpening” - a boolean.

In the initialisation do something like

IsOpening = true me.Value = Globals.thePrefs.GetPrefBoolean("ShowHelp") IsOpening = False

Setting the value triggers the modified event. There you do

if not IsOpening then Globals.thePrefs.SetPrefBoolean("ShowHelp", me.value) uMgr.record action end if

So that open and modified don’t interfere with each other.

HTH

Works like a charm! Thanks.