将 PowerShell 输出(碰巧)写入 WPF UI 控件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23142137/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Write PowerShell Output (as it happens) to WPF UI Control
提问by jmcdade
I've been reading blogs about writing to the UI from different runspaces (http://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-ui-from-a-different-runspace/).
我一直在阅读有关从不同运行空间写入 UI 的博客(http://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-ui-from-a -不同的运行空间/)。
I'm basically trying to make it so I can click a button in the UI and run a PowerShell script and capture the output of that script as it happens and update the WPF UI control without freezing up the UI.
我基本上是在尝试这样做,以便我可以单击 UI 中的一个按钮并运行 PowerShell 脚本并在发生时捕获该脚本的输出,并在不冻结 UI 的情况下更新 WPF UI 控件。
I've tried a basic example of just writing some output directly, but it seems to hang the UI. I'm using runspaces and dispatcher, but I seem to be stuck on something.
我已经尝试了一个直接编写一些输出的基本示例,但它似乎挂起了 UI。我正在使用运行空间和调度程序,但我似乎遇到了一些问题。
Any ideas?
有任何想法吗?
Thanks.
谢谢。
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")
}
})#>
采纳答案by Doug
Out of curiosity, have you considered that instead of PowerShellfiring up WPFand displaying some output, it should be the other way around?
出于好奇,您是否考虑过PowerShell不应该启动WPF并显示一些输出,而应该相反?
Perhaps you should be invoking PowerShellfrom within a WPFapplication, and capturing the output to be displayed?
也许您应该从WPF应用程序中调用PowerShell,并捕获要显示的输出?
http://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C
http://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C
回答by iRon
I ran into the same question and seen quiet some suggestions that either refer to C# classes or examples where the UI is placed in the worker instead of the actual worker task. Anyway, after spending quite some time I figured it out:
我遇到了同样的问题,并看到了一些安静的建议,这些建议要么是指 C# 类,要么是将 UI 放置在工作人员中而不是实际工作人员任务中的示例。无论如何,在花了很长时间之后我想通了:
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()
For a windows control example see: PowerShell: Job Event Action with Form not executed
有关 Windows 控件示例,请参阅:PowerShell:未执行表单的作业事件操作
回答by boeprox
I made some adjustments in your code. I wouldn't run your For loop within the dispatcherand instead would run the dispatcher block in the For loop. I tested and it didn't seem to lock up for me (had some moments where the performance wasn't the best, but that was expected) and kept writing to the window.
我对你的代码做了一些调整。我不会在调度程序中运行您的 For 循环,而是在 For 循环中运行调度程序块。我进行了测试,但它似乎并没有锁定我(有些时候性能不是最好的,但这是预期的)并继续写入窗口。
for ($i = 0; $i -lt 10000; $i++) {
$uiHash.OutputTextBox.Dispatcher.Invoke("Normal", [action]{
$uiHash.OutputTextBox.AppendText("hi")
})
}
回答by pianoboy
This may be a little late but try downloading WPFRunspace, which provides a Powershell backgroundworker cmdlet for WPF and Win Forms GUI scripts that will execute in the background and update your GUI either as output is received or on completion.
这可能有点晚了,但请尝试下载WPFRunspace,它为 WPF 和 Win Forms GUI 脚本提供了 Powershell 后台工作程序 cmdlet,这些脚本将在后台执行并在接收到输出或完成时更新您的 GUI。

