我一直在阅读有关从不同的运行空间(http://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-ui-from-a-different-runspace/)写入UI的博客。
我基本上是想要这样做,所以我可以点击UI中的按钮并运行PowerShell脚本并捕获该脚本的输出,并在不冻结UI的情况下更新WPF UI控件。
我尝试过直接编写一些输出的基本示例,但它似乎挂起了UI。我正在使用运行空间和调度程序,但我似乎陷入困境。
有什么想法吗?
感谢。
Add-Type –assemblyName PresentationFramework
Add-Type –assemblyName PresentationCore
Add-Type –assemblyName WindowsBase
$uiHash = [hashtable]::Synchronized(@{})
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable('uiHash',$uiHash)
$psCmd = [PowerShell]::Create().AddScript({
[xml]$xaml = @"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window" Title="Patcher" Height="350" Width="525" Topmost="True">
<Grid>
<Label Content="A Builds" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="88" RenderTransformOrigin="0.191,0.566"/>
<ListBox HorizontalAlignment="Left" Height="269" Margin="10,41,0,0" VerticalAlignment="Top" Width="88"/>
<Label Content="New Build" HorizontalAlignment="Left" Margin="387,10,0,0" VerticalAlignment="Top"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="387,41,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label Content="B Builds" HorizontalAlignment="Left" Margin="117,10,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.528,-0.672"/>
<ListBox HorizontalAlignment="Left" Height="269" Margin="103,41,0,0" VerticalAlignment="Top" Width="88"/>
<Label Content="C Builds" HorizontalAlignment="Left" Margin="185,10,0,0" VerticalAlignment="Top"/>
<ListBox HorizontalAlignment="Left" Height="269" Margin="196,41,0,0" VerticalAlignment="Top" Width="88"/>
<Button x:Name="PatchButton" Content="Patch!" HorizontalAlignment="Left" Margin="426,268,0,0" VerticalAlignment="Top" Width="75"/>
<RichTextBox x:Name="OutputTextBox" HorizontalAlignment="Left" Height="194" Margin="289,69,0,0" VerticalAlignment="Top" Width="218">
<FlowDocument>
<Paragraph>
<Run Text=""/>
</Paragraph>
</FlowDocument>
</RichTextBox>
</Grid>
</Window>
"@
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$uiHash.Window = [Windows.Markup.XamlReader]::Load($reader)
$uiHash.Button = $uiHash.Window.FindName("PatchButton")
$uiHash.OutputTextBox = $uiHash.Window.FindName("OutputTextBox")
$uiHash.OutputTextBox.Dispatcher.Invoke("Render", [Windows.Input.InputEventHandler] {$uiHash.OutputTextBox.UpdateLayout()}, $null, $null)
$uiHash.Window.ShowDialog() | Out-Null
})
$psCmd.Runspace = $newRunspace
$data = $psCmd.BeginInvoke()
# The next line fails (null-valued expression)
<#
$uiHash.OutputTextBox.Dispatcher.Invoke("Normal", [action]{
for ($i = 0; $i -lt 10000; $++) {
$uiHash.OutputTextBox.AppendText("hi")
}
})#>
答案 0 :(得分:1)
我遇到了同样的问题并且看到了一些建议,这些建议要么引用C#类,要么将UI放在worker中而不是实际的worker任务中。 无论如何,在花了相当长的一段时间后我才明白:
Add-Type -AssemblyName PresentationFramework
$Xaml = New-Object System.Xml.XmlNodeReader([XML]@"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test" WindowStartupLocation = "CenterScreen" ShowInTaskbar = "True">
<Grid>
<TextBox x:Name = "TextBox"/>
</Grid>
</Window>
"@)
Function Start-Worker {
$TextBox = $Window.FindName("TextBox")
$SyncHash = [hashtable]::Synchronized(@{Window = $Window; TextBox = $TextBox})
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.ThreadOptions = "ReuseThread"
$Runspace.Open()
$Runspace.SessionStateProxy.SetVariable("SyncHash", $SyncHash)
$Worker = [PowerShell]::Create().AddScript({
for($Progress=1; $Progress -le 10; $Progress++){
$SyncHash.Window.Dispatcher.Invoke([action]{$SyncHash.TextBox.AppendText($Progress)})
Start-Sleep 1 # Some background work
}
})
$Worker.Runspace = $Runspace
$Worker.BeginInvoke()
}
$Window = [Windows.Markup.XamlReader]::Load($Xaml) # Requires -STA mode
$Window.Add_Loaded({Start-Worker})
[Void]$Window.ShowDialog()
有关Windows控件示例,请参阅:PowerShell: Job Event Action with Form not executed
答案 1 :(得分:0)
出于好奇,您是否考虑过而不是 PowerShell 启动 WPF 并显示一些输出,它应该是相反的方式?
也许您应该从 WPF 应用程序中调用 PowerShell ,并捕获要显示的输出?
http://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C
答案 2 :(得分:0)
我在你的代码中做了一些调整。我不会在调度程序中运行For循环,而是在For循环中运行调度程序块。我测试了它似乎没有为我锁定(有一些表现不是最好的,但这是预期的)并继续写入窗口。
for ($i = 0; $i -lt 10000; $i++) {
$uiHash.OutputTextBox.Dispatcher.Invoke("Normal", [action]{
$uiHash.OutputTextBox.AppendText("hi")
})
}
答案 3 :(得分:0)
这可能有点晚,但请尝试下载WPFRunspace,它为WPF和Win Forms GUI脚本提供Powershell后台工作cmdlet,该脚本将在后台执行并在收到输出或完成时更新GUI。