Page 1 of 2

How to Write From one Thread to Another ?

Posted: Tue Mar 08, 2016 12:49 pm
by PGilbert
While running a multithread WPF application that has the GUI running on one thread and the business code on another, it is not allowed that the business thread writes directly to the GUI thread.

Question: How do we do that then ?

Thanks in advance,

Pierre Gilbert

Re: How to Write From one Thread to Another ?

Posted: Tue Mar 08, 2016 6:37 pm
by Phil Last
When APL threads were new Jake solved this very problem very neatly for us by having the gui thread poll a set of nss each of which was allocated to another thread that wrote named variables to it asynchronously. The gui thread picked these up and displayed the data using a generic protocol.

No doubt more sophisticated solutions have become available since.

Re: How to Write From one Thread to Another ?

Posted: Thu Mar 10, 2016 12:55 am
by PGilbert
From a discussion with Michael Hughes it was recommended to use Data Binding or a Dispatcher to write values across threads.

Thanks Michael.

Re: How to Write From one Thread to Another ?

Posted: Thu Mar 17, 2016 7:40 pm
by PGilbert
Like Michael told me, it is explained on the web that you need a dispatcher to access the properties of an object that is on another thread. This is such an example in C#:

Code: Select all

textBox.Dispatcher.Dispatcher.Invoke(
      System.Windows.Threading.DispatcherPriority.Normal,
      new Action(
        delegate()
        {
          textBox.Text = "Hello World";
        }
    ));


From the WPF WS that Michael supplied me, I am trying to do a simplistic function that will do something similar in APL. So far I have this:

Code: Select all

 obj Dispatch action;ActionObj;delegate;⎕USING

 action ← ⎕FX 'RunBackground' action

 ⎕USING ← 'System,System.dll' 'System.Windows,WPF/WindowsBase.dll' 'System.Windows.Threading,WindowsBase.dll'

 action    ← Type.GetType⊂'System.Action`1'
 ActionObj ← action.MakeGenericType(⊂,Type.GetType⊂'System.Object')
 delegate  ← 2016⌶ActionObj(⎕OR'RunBackground')
 obj.Dispatcher.BeginInvoke((DispatcherPriority.Normal)delegate)


and ideally I would like to do the following:

      ⎕USING←'System.Windows,WPF/PresentationFramework.dll'
win←⎕NEW Window
win Dispatch 'win.Title←''hello world'''
EXCEPTION
Dispatch[9] obj.Dispatcher.BeginInvoke((DispatcherPriority.Normal)delegate)

⎕EXCEPTION.Message
VALUE ERROR
⎕EXCEPTION.Message


What would be the correct way to write the function 'Dispatch' ?

Thanks in advance.

Re: How to Write From one Thread to Another ?

Posted: Thu Mar 17, 2016 9:07 pm
by PGilbert
Found it:

      ∇Dispatch[⎕]∇
[0] obj Dispatch action;ActionObj;delegate;⎕USING
[1]
[2] action←⎕OR ⎕FX'RunBackground'action
[3]
[4] ⎕USING←'System,System.dll' 'System.Windows.Threading,WindowsBase.dll'
[5]
[6] {}obj.Dispatcher.BeginInvoke(DispatcherPriority.Normal(⎕NEW Action action))

⎕USING ← 'System.Windows,WPF/PresentationFramework.dll'
win ← ⎕NEW Window
win Dispatch& 'win.Title←''hello world'''

win.Title
hello world


The problem that I see is that all actions will be put into the same function named 'RunBackground' and if you do a couple of rapid Dispatch one after each one the 'RunBackground' executed may not be the good one.

Is there a way to fix this ?

Re: How to Write From one Thread to Another ?

Posted: Thu Aug 18, 2016 1:39 am
by PGilbert
Following from my previous post, looks like the Dispatcher idea can have currently some issues with the interpreter (based on my experience so far). I have resort to experiment with binding an apl variable to the DataContext of a form. The apl variable can contain a DataTable that can be used for binding (setting the DataContext directly with a DataTable across threads is not permitted in WPF). That way you can assign a DataTable to the apl variable across threads and the bind variable will change the DataContext with the value of the DataTable.

So far my experimentation has led me to the following function for this particular type of binding:

Code: Select all

 object BindVarNameToObjDataContext varName;binding;⎕USING
⍝ Binds a fully qualified existing apl variable name to the DataContext of a .Net objet.
⍝ The apl variable can contain a .Net object

⍝ varName = fully qualified existing apl variable name
⍝ object  = .Net object

 ⎕USING←'System.Windows.Data,WPF/PresentationFramework.dll' 'System.Windows,WPF/PresentationFramework.dll'

⍝ This construction will set the 'Path' of the binding that is the name of the variable without the path
⍝ The 'Path' property is a 'System.Windows.PropertyPath' that is easier to set in the constructor than as a property.
 binding←⎕NEW Binding(⊂,{(-⊥⍨⍵≠'.')↑⍵}varName)  ⍝ varName is without the path here
 binding.Source←2015⌶varName                    ⍝ varName is fully qualified with path here
 binding.Mode←BindingMode.OneWay                ⍝ Setup as One Way Binding
 binding.UpdateSourceTrigger←UpdateSourceTrigger.PropertyChanged

⍝ Setting-up the Binding to the DataContext Property:
⍝ Looks like not all the elements have a DataContextProperty even if they show it in the intellisense.
⍝ We need to use instead a FrameworkElement that has always a DataContextProperty available to do the Binding.
⍝ Using object.SetBinding is very much faster than Data.BindingOperations.SetBinding(object FrameworkElement.DataContextProperty bd)

 {}object.SetBinding(FrameworkElement.DataContextProperty binding)


All this because I have not been able to do a simple/direct binding with 2015⌶.

Question: Is there a simpler way to do this type of binding ?

Thanks in advance

Re: How to Write From one Thread to Another ?

Posted: Thu Aug 18, 2016 7:32 am
by MikeHughes
The way to do multiple bindings to variables is to bind a Namespace to a DataContext - not a DataTable

Re: How to Write From one Thread to Another ?

Posted: Thu Aug 18, 2016 11:49 am
by PGilbert
Hello Michael, I was meaning if there is a simpler way to write the function 'BindVarNameToObjDataContext'.

Re: How to Write From one Thread to Another ?

Posted: Fri Aug 19, 2016 7:08 am
by MikeHughes
Yes, the namespace binding does use the ibeam

:Trap 0
{}Reset ns
:If 0=×/⍴type ⋄ obj.DataContext←(2015⌶)ns ⍝ Assign DataContext
:Else ⋄ obj.DataContext←type(2015⌶)ns ⍝ Assign DataContext with export definition
:EndIf
:Else ⋄ 'Trouble setting DataContext'⎕SIGNAL 90 ⍝ or throw exception
:EndTrap


{r}←Reset name
⍝ Resets the binding on the variable or namespace named in the argument

r←(2014⌶)name
⍝ c mjh 26Sep2014

Re: How to Write From one Thread to Another ?

Posted: Fri Aug 19, 2016 12:06 pm
by PGilbert
Thanks Michael, in our case we want to show the value of a log file in our application. An item of the log file is some HTML with a message (color of the text will vary depending if it is an alarm or status) or a capture screen of the application (save as base64 in an inline png). Since the log file can be too big for the size of the workspace we save it in a CSV file outside the workspace (easy to add another item of the log that way too). We have found a third party dll that will read the CSV file and return a .net DataTable without having a single byte transiting in the workspace. Once we have the DataTable it is bind to a CollectionViewSource with a ListBox that contains some Syncfusion RichTextBoxAdv that will render the individual HTML items. The WPF ListBox will do some 'virtualizing' so we can see right away the items of the log that are in the view and the other items will be rendered only when we scroll (otherwise it is too long to wait for all the items to be rendered before viewing only the couple first ones).

We do on another thread the conversion of the CSV file to a DataTable and the binding hence my question on this forum.

It is a little bit hard to do the first time but the result is quite spectacular (the items of each day are group together in a custom .net Expander). There is a possibility that Dyalog will allow, in the future, the binding of a function. I think it would than be possible to bind a component file (via that function) and it will be much easier to do the same thing (looking forward for that Dyalog).

Thanks Michael for your help.