Window.Opening happens after App.Opening. Everything should be set up by then.
Huh, I must have misread the docs. I thought app.Opening was last. Is it in fact first or simply prior to Window.Opening ?
The sequence is
app.opening
window controls opening (in random order)
window.opening (before the window is displayed)
window.activated (after the window is displayed)
This may help:
Thanks Mike - I’ve tried that now and it works here too. It’ll be some days however before I get a chance to try adapting my app to follow that pattern.
I added a system.debuglog statement to app.Opening, and got this:
18:44:04 : windowLocation Launched
: Window1.Opening
: app.Opening finishing
18:44:05 : Window1.ScaleFactorChanged
: Window1.Moved 2560,53
: Window1.Resized 1680x997
: Window1.Activated
: Window1.Paint
18:44:08 : Window1.Deactivated
18:44:12 : windowLocation Ended
You will note that the reference to Window1 in the callLater causes app.Opening to be interrupted and the window with its conrtols to run their Opening events. Only after that does app.Opening continue and finish. So the CallLater wll have to be the last statement in app.Opening.
The thing to beware of there is that Activated fires each time the window becomes the frontmost window.
Trying to use CallLater with App opening will lead to unpredictable results, the time required to activate everything can vary greatly. I would strongly suggest moving the window in Window.Opening event, rather than trying to fudge things to work in App.opening.
I’m not doing anything at the moment, just playing about to see how things work. The take-away at now is to ensure that I don’t reference the window, or any of its controls in app.Opening. It’ll take me a while to move app.Opening to that situation, because, mistakenly thinking that app.Opening happened last, rather than first, that’s where all the initialisation takes place at present, rather tthan in the window and others controls’ Opening events. There’s lots of stuff I can move, however.
Even so, I’m going to have to do something clever to let app.Opening do the rest of the initialisation, since it wants to report its progress and any errors encountered during startup. Some of that it wants to write - guess where - into a control within the window so the user can see the messages.
Not quite - Xojo’s event model guarantees (*) that any event will run to completion before the next event happens. So if you had called Timer.CallLater(1,...)
which implies that the handler would be called “1 millisecond” later, in fact the timer’s action won’t fire until the event loop has finished processing the current event (App.Opening, and any events triggered within App.Open).
(*) there are some rare exceptions to this rule, but generally it’s sound.
Tim points out a different issue, which is that if you reference any window that has implicitInstance = true, that window will be created at that point, and Window.Opening (and Control.opening, etc.) will all happen at that point.
The pausing of app.Opening if it reference’s the window, causing all controls + window’s Opening events to run, and the subsequent completion of app.Opening, appears to be one such exception.
I don’t think “pausing” is the right conceptual model here?
Code like this:
App.Open
Timer.CallLater(1, foobar)
Window1.show
End
Function App.Foobar
Will generate events like this:
App.opening
Window1.ControlX.Opening
[...]
Window1.Opening
App.Foobar
It doesn’t matter how long it takes for Window1 to open, the timer will always happen afterwards.
Unless you are messing with dark magic such as App.DoEvents
in which case, good luck to you!
But this is not what your own example has, is it. What you have is:
App.Opening
Timer.CallLater (1, AddressOf Window1.mySub)
End
I modified your example slightly, adding a system.debuglog after the CallLater, and another in the Opening of the control on the window. That shows the sequence:
App launched
window.control Opening
window Opening
app.Opening finishing.
While “pausing” may not be the word to use, it’s shorter than “finding that we have to deal with the window being referenced and thus veering off course to deal with all of that before resuming whatever app.Opening was doing”.
What Mike is pointing out is that referencing Window1 (a class name, rather than an instance of the class) causes an instance of the class to be generated. It is that instantiation that happens “inline”.
Interesting, I would have expected that using the AddressOf operator, as in
App.Opening
timer.callLater (1000, addressOf Window1.Relocate)
Should not instantiate the window, but it appears that it does actually do so, which is why Tim is seeing that event order.
To fix that, I would instead have the Timer.callLater function refer to an App method (not a Window1 method), in which case it works as expected:
7:15:00 AM : windowLocation Launched
: App.Opening begin
: App.Opening end
7:15:01 AM : App.Relocate begin
: Window1.Label1.Opening
: Window1.Opening
: Window1.ScaleFactorChanged
: Window1.Moved 2560,28
: Window1.Resized 2560x1412
: Window1.Activated
: App.Relocate end
: Window1.Paint
7:15:03 AM : Window1.Deactivated
I suppose what’s going on is that AddressOf needs the actual object instance and method signature, so there’s no way to refer to a method of an object that doesn’t yet exist, and so Window1 is created before we want it to be.
Here’s my modified sample:
windowLocation2.zip (6.4 KB)
OK, quite some progress so far. I’ve reworked my startup sequences so that in app.Opening (and methods it calls) there is only initialisation as it relates to the file system (not to any control on the window). So the app’s Opening event runs, then the Window’s, and finally the method via CallLater which decides where to move the window.
This all works as expected, and I see this sequence:
14:17:55 : iLetter Launched
14:17:56 : iLetter.Opening begin
: App Nap disabled for “App Nap Handling”.
: Initialise.startup begin
: Initialise.startup end
: iLetter.Opening end
: MainWindow.Opening begin
: MainWindow.Opening end
14:17:57 : iLetter.placeMainwindow begin
: MainWindow.Moved 2561, 59
: MainWindow.Resized 1663 x 981
: iLetter.placeMainwindow end
14:19:13 : iLetter Ended
However, if I try to use the additional trick of setting the window’s visibility to False in the IDE, then to True at the end of the move method, then there is an additional Moved event after the expected sequence:
14:21:26 : MainWindow.Moved 1, 449
So I’m arguably no further forward.
Show us all the settings for that window in the IDE. I wonder if you have something strange like the wrong frame style?
What is WindowClass?
Normally, in an API project the window would be a Window class, and in an API2 project it would be DesktopWindow
Do you have a Window subclass, perhaps one that has some code in it?
It’s something I added very recently (since this thread started), to support havng a Window menu in the app. It allows a DesktopWindow to register and de-register on the Window menu - handles having the tickmark against the active window in the menu, and the bringing of that window forward if the user selects it from the Window menu.