Multithreading 使用Powershell作业更新WPF GUI

Multithreading 使用Powershell作业更新WPF GUI,multithreading,powershell,powershell-jobs,Multithreading,Powershell,Powershell Jobs,我一直在尝试为我的个人Powershell脚本创建响应性GUI。我提出了一个在线讨论最多的问题:冻结GUI(因为Powershell是单线程的) 与此类似,但我的案例特定于Powershell。我成功地实现了一个基于Powershell的解决方案,用于创建依赖于XAML表单的GUI。现在,让我们来考虑这个代码: #EVENT Handler $Somebutton.add_Click({ $SomeLabel.Content = "Calculating..." Start-

我一直在尝试为我的个人Powershell脚本创建响应性GUI。我提出了一个在线讨论最多的问题:冻结GUI(因为Powershell是单线程的)

与此类似,但我的案例特定于Powershell。我成功地实现了一个基于Powershell的解决方案,用于创建依赖于XAML表单的GUI。现在,让我们来考虑这个代码:

#EVENT Handler
$Somebutton.add_Click({
    $SomeLabel.Content = "Calculating..." 

    Start-Job -ScriptBlock {
        #Computation that takes time
        #...
        $SomeLabel.Content = "Calculated value" 
    }
})

#Show XAML GUI
$xamlGUI.ShowDialog() | out-null
xamlGUI
是表单本身,
$Somebutton
/
$SomeLabel
是我能够从xaml读取并转换为Powershell变量的控件

我试图理解为什么我开始的工作在计算完成时不更新我的标签。它实际上什么也不做


我是新来Powershell工作的,我想知道我是否遗漏了什么

下面是我在PowerShell中用于反应式WPF表单的一个小模板:

# Hide yo console
$SW_HIDE, $SW_SHOW = 0, 5
$TypeDef = '[DllImport("User32.dll")]public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);'
Add-Type -MemberDefinition $TypeDef -Namespace Win32 -Name Functions
$hWnd = (Get-Process -Id $PID).MainWindowHandle
$Null = [Win32.Functions]::ShowWindow($hWnd,$SW_HIDE)

# Define your app + form
Add-Type -AssemblyName PresentationFramework
$App = [Windows.Application]::new() # or New-Object -TypeName Windows.Application
$Form = [Windows.Markup.XamlReader]::Load(
    [Xml.XmlNodeReader]::new([xml]@'
WPF form definition goes here
'@)
)
# or ::Load((New-Object -TypeName Xml.XmlNodeReader -ArgumentList ([xml]@'
#wpfdef
#'@))
#)

# Fixes the "freeze" problem
function Update-Gui {
    # Basically WinForms Application.DoEvents()
    $App.Dispatcher.Invoke([Windows.Threading.DispatcherPriority]::Background, [action]{})
}

# Event handlers go here
$Form.add_Closing({
    $Form.Close()
    $App.Shutdown()
    Stop-Process -Id $PID # or return your console: [Win32.Functions]::ShowWindow($hWnd,$SW_SHOW)
})

# Finally
$App.Run($Form)
请记住在应用程序关闭时进行清理:

$Form.Close()
$App.Shutdown()
Stop-Process -Id $PID

每当需要反映对GUI的更改时,请调用
更新GUI
函数。

一旦启动作业,它将在完全不同的上下文中运行。它不知道在窗体运行的上下文中内存中有哪些对象。理论上,我认为您应该能够将按钮传递给作业。感谢您的回答,我将使用以下方式传递标签:
-ArgumentList$SomeLabel
?大多数寻求与PowerShell并发的人都会在作业上使用运行空间,因为您可以更轻松地与作业进行通信。另外,我建议从
ShowDialog()
转向
$App=[Windows.Application]::new()$App.Run($Form)
您也需要将其作为scriptblock中的参数接受。我在powershell中手动更新WPF表单时使用的东西:
$App.Dispatcher.Invoke([Windows.Threading.DispatcherPriority]::Background,[action]{})
非常感谢您的回答!真不敢相信这还没开始。。。不过,我可能会选择基于c#的解决方案。在c#中使用多线程似乎要容易得多,在我看来,为了更轻松地处理运行空间,学习它是值得的。然后我可以用c#controls处理程序调用我的脚本,就像你在我的另一个线程中建议的那样。如果您对此也有任何意见,我们将不胜感激。再次感谢。@scharette C#绝对是最基本的表单以外的任何东西的选择。只是因为我好奇,它不是可以与
Application.DoEvents()
相比的吗?它经常被社区视为魔鬼(见@scharette是的,它是可比的(我在我的代码中留下了这样的评论)。在C#应用程序中,它被视为魔鬼,因为这不是设计wpf应用程序的方式,但在PowerShell中,它需要付出更多的努力才能完成与C#相同的任务,所以我将其作为一个捷径。谢谢你的洞察力!