【问题标题】:Write PowerShell Output (as it happens) to WPF UI Control将 PowerShell 输出(发生时)写入 WPF UI 控件
【发布时间】:2014-04-17 19:47:22
【问题描述】:

我一直在阅读有关从不同运行空间 (http://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-ui-from-a-different-runspace/) 写入 UI 的博客。

我基本上是在尝试这样做,以便我可以单击 UI 中的按钮并运行 PowerShell 脚本并在脚本发生时捕获该脚本的输出并更新 WPF UI 控件而不会冻结 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")
    }
})#>

【问题讨论】:

    标签: wpf powershell dispatcher runspace


    【解决方案1】:

    出于好奇,您是否考虑过不是 PowerShell 启动 WPF 并显示一些输出,而是应该反过来?

    也许您应该从 WPF 应用程序中调用 PowerShell,并捕获要显示的输出?

    http://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C

    【讨论】:

    • 是的,出于好奇,我想看看我是否可以这样做。我并没有真正拘泥于某一种方式。
    • 尝试基于这种方法来适应 WPF 解决方案是一件非常头疼的事情。出于我的目的,我将在 WinForms 中托管 PowerShell 脚本并完成它。
    【解决方案2】:

    我遇到了同样的问题,并且看到了一些建议,这些建议要么是指 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

    【讨论】:

      【解决方案3】:

      我对您的代码进行了一些调整。我不会在调度程序中运行您的 For 循环 ,而是在 For 循环中运行调度程序块 。我进行了测试,它似乎并没有锁定我(在某些时候性能不是最好的,但这是意料之中的)并一直写到窗口。

      for ($i = 0; $i -lt 10000; $i++) {
          $uiHash.OutputTextBox.Dispatcher.Invoke("Normal", [action]{
              $uiHash.OutputTextBox.AppendText("hi")
          })
      }
      

      【讨论】:

      • 是的,我发现这很困难。当我尝试在与其余部分相同的脚本中使用该块时,我无法访问 ui 元素,但它在 ui 运行时通过控制台工作。这是预期的吗?无论如何,有没有更好的方法来提高性能,或者我应该按照 Doug 的建议颠倒顺序?
      • 这真的取决于你自己的个人喜好。有人会说你应该用 C# 而不是 PowerShell 编写 UI(就像 Doug 建议的那样),但我更喜欢用 PowerShell 编写 UI,并确保我在编码时小心谨慎,以提防针对 UI 运行命令的任何问题.如果您对自己的 C# 编码有信心,那么您可以试一试,否则请继续按照您当前的路径前进。
      • UI 线程是否可以处理连续的管道输出,或者它是否会被阻止?我设想在文本框中输出脚本,这样用户就可以密切关注,但仍然可以使用 UI 做其他事情。如果脚本的输出会占用所有内容,我将需要一种新方法。
      【解决方案4】:

      这可能有点晚了,但请尝试下载 WPFRunspace ,它为 WPF 和 Win Forms GUI 脚本提供了一个 Powershell backgroundworker cmdlet,这些脚本将在后台执行并在收到输出或完成时更新您的 GUI。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-30
        • 1970-01-01
        • 2012-12-18
        相关资源
        最近更新 更多