Accessing UI elements from a Thread using events

Having gone through the process of converting threaded UI call to a combination of timers & threads, it certainly would be nice if the Xojo language had a built-in way to say “Execute this method with these parameters on the main thread”. There isn’t, so I ended up writing my own, which wasn’t terribly difficult, but it feels like a bit of a hack, as compared to something that could be baked into the language syntax itself.

We initially did have that in some betas
Folks hated it so much we removed it

A thread with a timer added to it at run time that calls a method in your thread subclass (via add handler) will do just that
A really simple case might look like the following
There are some things to still deal with BUT this is a pattern you can use fairly readily

[code]Class MyThread
private mTimer as Timer

   Event Definition DoMyStuff

   Sub Run()
          mTimer = new Timer
           AddHandler mTimer.Action, AddressOf timerCallback
           mTimer.Period = 20
           mTimer.Mode = Timer.ModeMultiple
           
           // now do whatever in my run event
           for i as integer = 0 to 10000000

           next
   End Sub

   Private Sub timerCallback(which as Timer)
         RaiseEvent DoMyStuff
   End Sub

End Class
[/code]

No you can respond to the event DoMyStuff on a UI and KNOW for sure that you are NOT in the thread context
Timers are ALWAYS run on the main thread

[quote=112590:@Norman Palardy]We initially did have that in some betas
Folks hated it so much we removed it
[/quote]

Are we talking about the same thing? I seem to remember an early beta where the event would not be queued for later, but wold be immediately run-on the main thread, pausing the calling thread. This didn’t work for many situations.

If there was a “QueueThisMethodForExecutionOnTheMainThreadAsynchronously” feature, I bet I would have liked it :slight_smile:

The term “event” is somewhat ambiguous. In Xojo, an event is a method that can only be called by the class that defined it. It is a method call. Period. Some events are called in response to OS events and are called from the “event loop” on the main thread. But the salient point here is that no event you define yourself (in this case, in a thread subclass) will automatically be called from the event loop. All events that you define in a class are straight method calls. There is no magic that puts them into the event loop.

On this topic, I was wondering if there’s a way to get a thread’s destructor to be called when a window is closed or does it need to have a close method called from the window’s close event?

I’m wondering because I was messing with a generic thread class that creates a timer that raises an UpdateUI event. Only problem is that if the window closes while the thread is running, it continues after the controls have closed and causes a NilObjectException to be raised. I suppose I could handle the exception and kill the thread, but it seems like there should be a cleaner way…

[quote=112602:@jim mckay]On this topic, I was wondering if there’s a way to get a thread’s destructor to be called when a window is closed or does it need to have a close method called from the window’s close event?

I’m wondering because I was messing with a generic thread class that creates a timer that raises an UpdateUI event. Only problem is that if the window closes while the thread is running, it continues after the controls have closed and causes a NilObjectException to be raised. I suppose I could handle the exception and kill the thread, but it seems like there should be a cleaner way…

[/quote]
Check to see if the thread is still running, Thread.State, and if running, kill the thread when closing the Window.

[quote=112591:@Michael Diehr]
If there was a “QueueThisMethodForExecutionOnTheMainThreadAsynchronously” feature, I bet I would have liked it :)[/quote]
I think there’d be a lot of missing toes :stuck_out_tongue:

[quote=112602:@jim mckay]On this topic, I was wondering if there’s a way to get a thread’s destructor to be called when a window is closed or does it need to have a close method called from the window’s close event?

I’m wondering because I was messing with a generic thread class that creates a timer that raises an UpdateUI event. Only problem is that if the window closes while the thread is running, it continues after the controls have closed and causes a NilObjectException to be raised. I suppose I could handle the exception and kill the thread, but it seems like there should be a cleaner way…[/quote]

There isn’t

Let me adjust your phrase for my reality: <The term “event” confuses people in Xojo, where firing events usually are just calls to its handlers, but I don’t know why many people mistakenly call them (the handlers) as events too>

:stuck_out_tongue:

I wonder why there isn’t in Xojo some kind of a built-in window event queue mechanism, a window.eventqueue array, you would send/add events to a queue with window.addeventtoqueue() and the window itself automatically would raise/execute those events in the main thread in the same order they have been added, taking care of and passing the event parameters, controlling the queue itself…etc… I know, that would be a built-in array of events and a timer, I can do it myself… but Xojo could provide it built-in I believe. You could even detect thread Accessing UI exceptions silently and add the event to the queue rather that showing the ThreadAccessingUIException exception.

… also… why reading a listbox.listcount also throw a ThreadAccessingUIException exception? Reading modifies nothing… and in this case it is a read only property :-/ You can’t even read a listbox cell data, everything cause ThreadAccessingUIException exceptions. How do you read stuff then? Local variables? A mirror array of list boxes?

The exception is ThreadAccessingUIException, not just Reading.

Oh, and ListCount IS writable.

Sorry. My assumption was made based on an identically named method created by another engineer. ListCount is not writeable.

from the LR:

ListBox.ListCountFrom Xojo Documentation
Jump to: navigation, search
Property (As Integer)The property is read-only.

IntegerValue = aListBox.ListCount

Unfortunately the docs are incorrect.

See above.

ListCount is readonly. This will not compile:

// Open event of a listbox instance Me.ListCount = 10
Error message of the compiler: Cannot assign a value to this property

Well, that’s interesting. I know I’ve seen that done in our products. When I get back from lunch I’ll have a look at that code and see what’s going on.

Might it be that you were thinking of WebListbox’s RowCount?

Wouldn’t setting the ListCount value be useless since it is completely dependent on how many items (rows) are in a list?

So my solution was to add a CancelClose method and event to the thread subclass… Then, called from the window.cancelclose event, I have the opportunity for the thread (in the thread’s cancelClose eventHandler) to prevent the window from closing, or allow the user to wait for the thread or kill it (in the thread’s cancelClose method)… Any UI stuff is handled in an event in the thread instance raised from a timer’s action event. The timer is set from the thread whenever the UI needs to be updated. The timer is created in the thread class’s constructor with addHandler. Works very well!