Using Terminal.Gui I'm trying to set a TextView's text from a asynchronous function, I can do it in C# just fine from a task but not from a Powershell Job, there might be a PS concept I'm missing here.
TextView tv = new TextView();
Task.Run(() =>
{
Thread.Sleep(5000);
tv.Text = "task";
Application.DoEvents();
});
I've tried running a job script block
Start-Job -ScriptBlock { $textView.Text = "task" }
And also tried to invoke it from the MainLoop, not sure I'm doing this properly but it looks like this
function FromJob {
param (
[Terminal.Gui.TextView] $tv
)
$delegate = [System.Action]{
$tv.Text = "Delegate"
}
[Terminal.Gui.Application]::MainLoop.Invoke($delegate)
}
Start-Job -ScriptBlock { FromJob } -ArgumentList @($textView)
As explained in comments, the main issue with your code is that:
FromJob
function doesn't exist in the scope of your Job, it would preferably need to defined inside it or the definition of the function should be passed as argument and then defined in that scope.Sart-Job
Jobs run in a different PowerShell process and has no access to update a reference variable passed to it. All arguments passed to these jobs are serialized and then deserialized and are no longer the same reference.To overcome the point 2, the easiest option is with Start-ThreadJob
which executes in the same process in a different runspace. Also, in this case I don't see the need for a function.
$job = Start-ThreadJob -ScriptBlock {
# brings the locally defined variable to this scope
$textView = $using:textView
$delegate = [System.Action]{
$textView.Text = "Delegate"
}
[Terminal.Gui.Application]::MainLoop.Invoke($delegate)
}
The other option is with a PowerShell instance which comes in handy if for some reason you can't install the ThreadJob Module and are using Windows PowerShell 5.1 (it comes preinstalled in newer versions).
$ps = [powershell]::Create().AddScript({
param([Terminal.Gui.TextView] $tv)
$delegate = [System.Action]{
$tv.Text = "Delegate"
}
[Terminal.Gui.Application]::MainLoop.Invoke($delegate)
}).AddParameter('tv', $textView)
$async = $ps.BeginInvoke()