UI updating

Hi,

I am migrating from Realbasic 2009 to Xojo.
My rb2009 application does build (for windows) but I also get
lots of ThreadAccessingUIException errors when running.

I understand Xojo simply does not allow UI updating directly
from within a Thread, but I have to use either a Timer or a Task.

The Task seems best as there are many UI items to be updated.
I have a Thread that monitors the serial port for incoming messages.
These are decoded and the required UI items (sliders, textboxes, textareas, buttoncaptions, colors etc)
are directly updated.

If I follow the Task, I need to call the UpdateUI() method with a Pair or Dictionary, identifying the UI item and its new value.

First, can I change the used named thread (with Super Thread ) simply to have Super Task, moving code from Run
(as Thread) to Run (as Task), and then add code to the UpdateUI() method?

Suppose I have UpdateUI(Args as Dictionary)

Within the UpdateUI() method, I must then have a sequence of statements like
If args.HasKey(“UIProgress”) Then
UIProgress.Value = args.Value(“UIProgress”).IntegerValue
End If

(the above is from the Xojo example).

My thread literally updates dozens of UI items. I hate to have to go through
all the if statements every time the UI needs to be updated.
In RB it was nice to keep code together and not splattered around.
(I still think the programmer should have the option to directly update the UI,
or have a API call available to do that, rather than Xojo decide that).

What options are there to not have to go through the numerous ifs ?
(and having a Task for every UI update isn’t quite what I am looking for).

regards peter

I would use Interfaces with an UpdateUI method that call internally the UI classes that needs to be updates. There are other OOP approaches, but IMO Interfaces are key in OOP for this kind of purposes (among others, of course).

Hi Amando,

How would interfaces work?

In the end it still is the thread that must call a method in the mainthread,
so the mainthread can update the UI.
In the original setup, the thread itself could update the UI, which was
nice because the thread was sort of independent, there was no need
for the mainthread to know about the thread, other than to start it.

regards peter

This is what I would use for Interfaces, calling the UpdateUI for each UI that needs to be updated in the mainThread.

Xojo uses now Threads (before Fibers) and for accessing UI elements within a Thread, the OS would be need to be Thread Safe, that is not the case with soon 4 different platforms. The Task/Timer runs in the mainThread only for updating the UI, while thread runs the calculation.

What Xojo has done is the correct way, while RealStudio way of updating UI within a Thread were not correct and could lead to big problems.

You could set up a flag in the Thread as a boolean, loop for all the interface members, check the flag and call the method Interface.UpdateUI, and reset the boolean once update. As I said, there can be multiple ways, surely more elegant and efficient that the mine. Just I follow the OOP principle of “has a relation” and “is a member” for Interfaces and subclasses.

Sgrunt, can’t edit my post…

“You could set up a flag in the Thread as a boolean”, I meant “You can set a boolean flag in every member of the Interface”…

Interface
NeedsUpdating(boolean)
UdateUI()

Or you can iterate all, depends of your application, number of controls, etc.

Hi,

Looking at the UIThreadingWithTask example,

There is a mainwindow (mainthread) that also has defined UITask.
There is a startbutton that starts UITask.run

In my original code I have a thread Thread1 that is automatically
started from mainwindow by calling Thread1.run

So what if I declared my Thread1 to be of class Task instead of Thread, and put its run
code in the task.run section.
I would still start it as Thread1.run (just not using a button to start it).

Now Task class has the extra methods UpdateUI(args as Dictionary)
and UpdateUI(paramarray args as Pair)
either of which can be used to update the UI.

In the example, UpdateUI() updates a progressbar.

Can I add a method UpdateUI(value as integer) and then use
Me.UpdateUI(progressValue) to update the UI

and in the UpdateUI(value as integer) just have
UIProgress.Value = value
No need for dictionary or pair, as I know which UI item needs to be updated.

If this can be done, I can add several methods using different names
to update the UI items I need to update, without the need for lengthy if sequences.
I could have
UpdateUImyButtonCaption(string caption)
myButton.caption = caption

so basically, in my thread code
I replace
myButton.caption = caption
by
UpdateUImyButton(caption)

regards peter

Hi Peter,

Well, I don’t use the Task approach example for my project, as I really use Monkeybreadsoftware (MBS) plugins in almost all my projects. For this in particular I post you will need to purchase MBS Util (If you can the complete collection is a masterpiece) for accessing the UI elements inside a Thread

I always use ContainerControls inside Window (I can apply interfaces to them), so I can access all members and Interfaces as I mentioned earlier. Like to reuse as much code as possible, and almost all GUI projects have some elements in common.

I have made a little demo to show how to achieve what you look for, easier than using my solution and valid for every thread. Just run the logic inside the thread, and call the UpdateUI for showing the UI changes.

There is a Window and ProgressBar1 and a Thread1 inside.

Thread Run

  Static counter as UInt32
   While True
    
    if counter mod 500 = 0 then
      // This calls a method in the mainThread as a delegate
      CallDelegateOnMainThreadMBS(AddressOf me.UpdateUI)
      me.Sleep(10)
      
    end if

    counter = counter + 1

  Wend

Note that you can’t access the window objects inside the thread logic or you will get an Exception

Inside the same Thread class I have UpdateUI method, where I can freely access the UI instances without Exceptions.

UpdateUI

  if Window1.ProgressBar1.Value <= Window1.ProgressBar1.Maximum then
    Window1.ProgressBar1.Value = Window1.ProgressBar1.Value + 5
  Else
    me.Suspend
  end if

Hope it helps

Amando

Great to see someone using it. With CallDelegateOnMainThreadMBS you could even pass parameters :slight_smile:

Great to know! We are always parameters hungry :slight_smile:

Hi,

I tried the Task approach but that didn’t work.
I also don’t see how UpdateUI() can retrieve property
settings or execute methods for UI items.

Here is run code for one thread that is executed by a buttonclick.
It sends out 254 ip address, hoping for a response.
I tried using 4 methods declared in the MainWindow and then
called from this thread, but I think these methods are then just
executed in the same thread, because it obviously doesn’t work.
(these are the 4 call with the original direct access as comment)

I like the delegate approach, with the local UpdateUI (could be any name I assume ??)
so I can keep code together.

  dim myIP as String
  dim localNet as String
  dim dotcount as integer
  dim index as integer
  dim testIP as string
  dim sData as Datagram
  dim mySock as UDPSocket
  dim Product_Id as string
  dim Firmware_Version as string
  dim Serial_No as string
  dim Mac_Address as string
  dim Hostname as string
  dim StaticIP as string
  dim StaticGateway as string
  dim StaticNetmask as string
  dim datalen as integer
  dim colindex as integer
  dim pos as integer
  dim listbox4row as integer
  
  MainWindow.SetBevelButton26(false) //MainWindow.Bevelbutton26.enabled = false
  mySock = new UDPSocket
  mySock.Port = 50001
  mySock.Connect()
  myIP = mySock.LocalAddress
  MainWindow.SetEditField12(myIP) //MainWindow.EditField12.text = myIP
  dotcount = 0
  index = 1
  localNet = ""
  while dotcount <> 3
    localNet = localNet + mid(myIP,index,1)
    if right(localNet,1) = "." then
      dotcount = dotcount + 1
    end if
    index = index + 1
  wend
  sData = new Datagram
  listbox4row = MainWindow.GetListbox4LastIndex //mainwindow.listbox4.lastindex
  while listbox4row > 0
    MainWindow.Listbox4RemoveRow(listbox4row) //MainWindow.Listbox4.RemoveRow(listbox4row)
    listbox4row = listbox4row - 1
  wend
  MainWindow.Popupmenu10.DeleteAllRows
  
  
  for index = 1 to 254
    testIP = localNet + str(index)
    if testIP <> myIP then
      MainWindow.EditField7.Text = testIP
      MainWindow.EditField7.refresh
      mySock.close()
      mySock = new UDPSocket
      mySock.Port = 50001
      mySock.Connect()
      mySock.Write(MainWindow.EditField7.Text,"hello"+chr(13))
      App.Delay_us(200000.0)
      if mySock.PacketsAvailable > 0 then
        App.myDatagram = mySock.read()
        MainWindow.Listbox3.addrow(App.myDatagram.Address)
        MainWindow.Listbox3.refresh
        datalen = App.myDatagram.Data.Len + 1
        colindex = 2
        pos = 1
        Product_Id = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          Product_Id = Product_Id + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        Firmware_Version = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          Firmware_Version = Firmware_Version + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        Serial_No = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          Serial_No = Serial_No + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        Mac_Address = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          Mac_Address = Mac_Address + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        Hostname = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          Hostname = Hostname + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        StaticIP = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          StaticIP = StaticIP + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        StaticGateway = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          StaticGateway = StaticGateway + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        StaticNetmask = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          StaticNetmask = StaticNetmask + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,1) = Product_Id
        MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,2) = Firmware_Version
        MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,3) = Serial_No
        MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,4) = Mac_Address
        MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,5) = Hostname
        MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,6) = StaticIP
        MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,7) = StaticGateway
        MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,8) = StaticNetmask
        MainWindow.Listbox4.addrow(App.myDatagram.Address)
        MainWindow.Listbox4.refresh
        MainWindow.ListBox4.CellType(MainWindow.Listbox4.LastIndex,0) = Listbox.TypeCheckBox
        MainWindow.ListBox4.Cell(MainWindow.ListBox4.LastIndex,1) = Hostname
        MainWindow.ListBox4.Cell(MainWindow.ListBox4.LastIndex,2) = "Disabled"
        MainWindow.Popupmenu10.addrow(App.myDatagram.Address)
        MainWindow.Popupmenu10.refresh
        
        
      end if
    end if
  next index
  mySock.close()
  MainWindow.Bevelbutton26.enabled = true

I would appreciate if you could alter some of the statements
to delegate statements, to show me how this thread code would
become.

regards peter

You won’t get anywhere this way (both code-wise and “please alter some of the statements”-wise).

Again: either use a Timer in the Thread subclass (a Timer always runs in the main thread, so you can modify the UI in there) or use the Task class. Just stating: “I tried the Task approach but that didn’t work.” is ridiculous - the Task class is working without any problems.

Btw, the change to not allow updating the UI from non-main-threads was not a decision made by Xojo Inc. See Accessing the User Interface from a Thread.

Hi Eli,

From what I see in the Task example, the UpdateUI only allows to set properties,
but I also need to execute methods and retrieve properties,
like in the statements
MainWindow.Popupmenu10.DeleteAllRows
and
listbox4row = mainwindow.listbox4.lastindex

So how is that supposed to work with Task?

And on the Timer approach, I must update many UI items, and the code in other threads
already know which items that are, so I find it strange that there has to be a Timer running
to check if a UI item must be updated (and it would have to check for all items that may be updated).
There must be a better way.

The choice to use Xojo has been made (we have the Pro license) so now I am trying to find
the best way to migrate our current applications, without too much hassle.

regards peter

The UIUpdate event could also be used to call a method, it is not limited to properties, only your imagination and implementation. For example if you had a method to update the listbox, then you could introduce the following in the UpdateUI event:

  If args.HasKey("UpdateLB4") Then
    Listbox4.deleteAllRows
    //update listbox contents
  End If

And in the run event whenever you want to update the listbox you could do:

//some test to know we need to update Listbox4
me.UpdateUI(“UpdateLB4”: “”)  //include parameters after the colon and you can access them in the UpdateUI event with args.Value(“UpdateLB4”)

Does that make sense?

Hi Jason,

I see how UpdateUI could be used to execute a method,
I would just have to define a Pair or Dictionary entry for every item property or method and check for
these in the UpdateUI method and then execute the correct statement.

But what about retrieving values? Would I need to declare shared
(or global) variables and accompanied boolean ResultUpdated in MainWindow, to hold
the retrieved results? And then in the thread wait for ResultUpdated to be true
(after calling UpdateUI) and copy (or use directly) the value in the global variable?

regards peter

Hi,

I am still unclear about this.
Maybe someone can get me a clear answer whether I am right
in the following.

Suppose I have a thread that must update and read 100 UI items and also must call UI methods.
When I use the Task class I will use statements like the following in the thread code
UpdateUI(“setproperty1”:value) //value is new value for property or address of memoryblock holding the new property value
UpdateUI(“getproperty1”:value) //value is address of placeholder for retrieved result
UpdateUI(“callmethod1”:value) //value=0 if no method args and no result, else address of placeholder for result and/or list of method args

The UpdateUI method will then look like

if args.HasKey("setproperty1) then
//code to set property1
property1.value = args.Value(“setproperty1”).IntegerValue //this assumes a simple value
return //I can immediately return
end if

if args.HasKey(“getproperty1”) then
//code to retrieve property1
dim result as string
result = property1.text //retrieve the result, could be any type
dim p1 as Ptr = args.Vaiue(getproperty1).IntegerValue //p1 is pointer to MemoryBlock (eg. the placeholder)
memcopy(p1,AddressOf result,sizeof(result)) //I don’t know the exact Xojo statement to copy, but this copies the result into the placeholder
return //I can return immediately
end if

if args.HasKey(“callmethod1”) then
//code to call method1
dim result as string
dim arg1 as integer
dim arg2 as integer
dim p1 as Ptr
p1 = args.Value(“callmethod1”).IntegerValue //p1 is address of list of result and args
//here must be code to retrieve args from the memoryblock
result = callmethod1(arg1, arg2)
memcopy(p1,AddressOf result, sizeof(result)) //copy result into placeholder
return //I can return immediately
end if

Is this what I am likely to end up with (some details may be wrong) ?
So when I have a 100 UI to update, I will have to check an average of 50
if args.HasKey()
for every UpdateUI call used in the thread.
This would be getting very ugly (not to mention slow), even with just 20 UI items to access.

Is there a way to overcome these if args.HasKey() testing,
other than using no threads or not updatng UI at all in threads.

regards peter

Hi,

Googling for better options (read: access UI easy and understandable from thread)
I found this
http://flapjacksaz.wordpress.com/2013/12/31/open-source-class-for-access-ui-objects-within-threads-in-xojo/

The code is open source and can be found here
https://bitbucket.org/mininetaz/xojo_threadsafeui_access/overview

The example thread code

dim c as integer
dim dc as integer
dim DPrams as new Dictionary
do
dim d as new date
ThreadSafeUIChange PushButton1,“Caption”,d.LongTime
c=c+1
if c=10 then
dim v as string
v=ThreadSafeGetUIProperty(PushButton1,“caption”)
ThreadSafeMsgBox v,me
c=0
end if
dc=dc+1
if dc=20 then
dim prams as new JSONItem
prams.Append “Method Pram pass”
ThreadSafeMethod window1,“msgtest”,prams
end if
DPrams.Value(“progressbar1.value”)=dc
ThreadSafeDelegate Addressof updateprogressbar,DPrams
if dc=100 then
ThreadSafeMethod me,“kill”
end if
me.Sleep 1000
loop

This looks more like it (better than Task or Timer anyway), as I can use the UI object names and property names
directly in the thread code. Also retrieving property values and calling
UI methods looks possible.

regards peter

As long as you understand what it’s doing, since you’ll need to tweak it for your needs, it’s all great.

This is inspired by the MsgBoxThreadSafe and essentially does the same thing. It take care of the coding that would raise exceptions all over the place and instead instantiating all those calls to timers which would in turn do what’s required.

It’s important to understand what it’s doing because some assumptions on how things may be running are incorrect, and in very tight loops or highly controlled sequences of actions weird results may happen (all the ones that justify the limitation and exception to begin with). For most uses it shouldn’tmake much of a difference (as it didn’t for years with RealStudio when it already supported threads).

I find it’s better to code using the new limitations but solutions like these are great for legacy apps that can’t be rewritten just yet.

Hi,

I had not to tweak the library, only my own direct statements,
and of course I had to add methods that do the actual work,
but these only contain the direct statements.

Here is the updated code (with direct access as comment)

  dim myIP as String
  dim localNet as String
  dim dotcount as integer
  dim index as integer
  dim testIP as string
  dim sData as Datagram
  dim mySock as UDPSocket
  dim Product_Id as string
  dim Firmware_Version as string
  dim Serial_No as string
  dim Mac_Address as string
  dim Hostname as string
  dim StaticIP as string
  dim StaticGateway as string
  dim StaticNetmask as string
  dim datalen as integer
  dim colindex as integer
  dim pos as integer
  dim listbox4row as integer
  dim DPrams as new Dictionary
  
  //MainWindow.Bevelbutton26.enabled = false
  ThreadSafeUIChange MainWindow.Bevelbutton26,"Enabled",false
  mySock = new UDPSocket
  mySock.Port = 50001
  mySock.Connect()
  myIP = mySock.LocalAddress
  //MainWindow.EditField12.text = myIP
  ThreadSafeUIChange MainWindow.EditField12,"Text",myIP
  dotcount = 0
  index = 1
  localNet = ""
  while dotcount <> 3
    localNet = localNet + mid(myIP,index,1)
    if right(localNet,1) = "." then
      dotcount = dotcount + 1
    end if
    index = index + 1
  wend
  sData = new Datagram
  //listbox4row = mainwindow.listbox4.lastindex
  listbox4row =ThreadSafeGetUIProperty(MainWindow.Listbox4,"lastindex")
  while listbox4row > 0
    //MainWindow.Listbox4.RemoveRow(listbox4row)
    dim prams as new JSONItem
    prams.Append listbox4row
    ThreadSafeMethod MainWindow,"Listbox4removerow",prams
    listbox4row = listbox4row - 1
  wend
  //MainWindow.Popupmenu10.DeleteAllRows
  ThreadSafeMethod MainWindow,"popupmenu10deleteallrows"
  
  
  for index = 1 to 254
    testIP = localNet + str(index)
    if testIP <> myIP then
      //MainWindow.EditField7.Text = testIP
      ThreadSafeUIChange MainWindow.EditField7,"Text",testIP
      //MainWindow.EditField7.refresh
      ThreadSafeMethod MainWindow,"editfield7refresh"
      mySock.close()
      mySock = new UDPSocket
      mySock.Port = 50001
      mySock.Connect()
      //mySock.Write(MainWindow.EditField7.Text,"hello"+chr(13))
      dim scanIP as String
      scanIP =ThreadSafeGetUIProperty(MainWindow.EditField7,"Text")
      mySock.Write(scanIP,"hello"+chr(13))
      App.Delay_us(200000.0)
      if mySock.PacketsAvailable > 0 then
        App.myDatagram = mySock.read()
        //MainWindow.Listbox3.addrow(App.myDatagram.Address)
        dim prams as new JSONItem
        prams.Append App.myDatagram.Address
        ThreadSafeMethod MainWindow,"listbox3addrow",prams
        //MainWindow.Listbox3.refresh
        ThreadSafeMethod MainWindow,"listbox3refresh"
        
        datalen = App.myDatagram.Data.Len + 1
        colindex = 2
        pos = 1
        Product_Id = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          Product_Id = Product_Id + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        Firmware_Version = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          Firmware_Version = Firmware_Version + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        Serial_No = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          Serial_No = Serial_No + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        Mac_Address = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          Mac_Address = Mac_Address + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        Hostname = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          Hostname = Hostname + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        StaticIP = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          StaticIP = StaticIP + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        StaticGateway = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          StaticGateway = StaticGateway + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        pos = pos + 1
        StaticNetmask = ""
        while (pos < datalen) and (mid(App.myDatagram.Data,pos,1) <> ",")
          StaticNetmask = StaticNetmask + mid(App.myDatagram.Data,pos,1)
          pos = pos + 1
        wend
        //MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,1) = Product_Id
        dim listbox3lastindex as integer
        listbox3lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox3,"LastIndex")
        dim prams3 as new JSONItem
        prams3.Append listbox3lastindex
        prams3.Append 1
        prams3.Append Product_Id
        ThreadSafeMethod MainWindow,"Listbox3cellwrite",prams3
        
        //MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,2) = Firmware_Version
        listbox3lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox3,"LastIndex")
        dim prams4 as new JSONItem
        prams4.Append listbox3lastindex
        prams4.Append 2
        prams4.Append Firmware_Version
        ThreadSafeMethod MainWindow,"Listbox3cellwrite",prams4
        
        //MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,3) = Serial_No
        listbox3lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox3,"LastIndex")
        dim prams5 as new JSONItem
        prams5.Append listbox3lastindex
        prams5.Append 3
        prams5.Append Serial_No
        ThreadSafeMethod MainWindow,"Listbox3cellwrite",prams5
        
        //MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,4) = Mac_Address
        listbox3lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox3,"LastIndex")
        dim prams6 as new JSONItem
        prams6.Append listbox3lastindex
        prams6.Append 4
        prams6.Append Mac_Address
        ThreadSafeMethod MainWindow,"Listbox3cellwrite",prams6
        
        //MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,5) = Hostname
        listbox3lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox3,"LastIndex")
        dim prams7 as new JSONItem
        prams7.Append listbox3lastindex
        prams7.Append 5
        prams7.Append Hostname
        ThreadSafeMethod MainWindow,"Listbox3cellwrite",prams7
        
        //MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,6) = StaticIP
        listbox3lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox3,"LastIndex")
        dim prams8 as new JSONItem
        prams8.Append listbox3lastindex
        prams8.Append 6
        prams8.Append StaticIP
        ThreadSafeMethod MainWindow,"Listbox3cellwrite",prams8
        
        //MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,7) = StaticGateway
        listbox3lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox3,"LastIndex")
        dim prams9 as new JSONItem
        prams9.Append listbox3lastindex
        prams9.Append 7
        prams9.Append StaticGateway
        ThreadSafeMethod MainWindow,"Listbox3cellwrite",prams9
        
        //MainWindow.ListBox3.Cell(MainWindow.ListBox3.LastIndex,8) = StaticNetmask
        listbox3lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox3,"LastIndex")
        dim prams10 as new JSONItem
        prams10.Append listbox3lastindex
        prams10.Append 8
        prams10.Append StaticNetmask
        ThreadSafeMethod MainWindow,"Listbox3cellwrite",prams10
        
        //MainWindow.Listbox4.addrow(App.myDatagram.Address)
        dim prams1 as new JSONItem
        prams1.Append App.myDatagram.Address
        ThreadSafeMethod MainWindow,"listbox4addrow",prams1
        
        //MainWindow.Listbox4.refresh
        ThreadSafeMethod MainWindow,"listbox4refresh"
        
        //MainWindow.ListBox4.CellType(MainWindow.Listbox4.LastIndex,0) = Listbox.TypeCheckBox
        dim listbox4lastindex as integer
        listbox4lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox4,"LastIndex")
        dim prams11 as new JSONItem
        prams11.Append listbox4lastindex
        prams11.Append 0
        prams11.Append Listbox.TypeCheckbox
        ThreadSafeMethod MainWindow,"Listbox4celltypewrite",prams11
        
        //MainWindow.ListBox4.Cell(MainWindow.ListBox4.LastIndex,1) = Hostname
        listbox4lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox4,"LastIndex")
        dim prams12 as new JSONItem
        prams12.Append listbox4lastindex
        prams12.Append 1
        prams12.Append Hostname
        ThreadSafeMethod MainWindow,"Listbox4cellwrite",prams12
        
        //MainWindow.ListBox4.Cell(MainWindow.ListBox4.LastIndex,2) = "Disabled"
        listbox4lastindex =ThreadSafeGetUIProperty(MainWindow.Listbox4,"LastIndex")
        dim prams13 as new JSONItem
        prams13.Append listbox4lastindex
        prams13.Append 2
        prams13.Append "Disabled"
        ThreadSafeMethod MainWindow,"Listbox4cellwrite",prams13
        
        //MainWindow.Popupmenu10.addrow(App.myDatagram.Address)
        dim prams2 as new JSONItem
        prams2.Append App.myDatagram.Address
        ThreadSafeMethod MainWindow,"popupmenu10addrow",prams2
        
        //MainWindow.Popupmenu10.refresh
        ThreadSafeMethod MainWindow,"popupmenu10refresh"
        
      end if
    end if
  next index
  mySock.close()
  //MainWindow.Bevelbutton26.enabled = true
  ThreadSafeUIChange MainWindow.Bevelbutton26,"Enabled",true
  

The scan thread now runs without issues.

regards peter