【问题标题】:Is there a way to send events to the parent job when using Start-WPFJob?使用 Start-WPFJob 时有没有办法向父作业发送事件?
【发布时间】:2015-02-20 04:08:59
【问题描述】:

我想从父 Powershell 脚本启动非阻塞 UI,并接收来自子作业的按钮点击等 UI 消息。我使用 WinForms 进行这种消息传递,但我更喜欢使用 ShowUI,因为创建基本 UI 所需的代码更少。但不幸的是,我还没有找到一种使用 ShowUI 将消息发送回父作业的方法。

[Works] 使用 Start-Job 时转发事件

使用Start-Job,将事件从子作业转发到父作业相当简单。这是一个例子:

$pj = Start-Job -Name "PlainJob" -ScriptBlock {
    Register-EngineEvent -SourceIdentifier PlainJobEvent -Forward
    New-Event -SourceIdentifier PlainJobEvent -MessageData 'My Message'
}

Wait-Event | select SourceIdentifier, MessageData | Format-List

正如预期的那样,它会打印出来:

SourceIdentifier : PlainJobEvent
MessageData      : My Message

[不工作] 使用 Start-WPFJob 时转发事件

另一方面,使用Start-WPFJob 似乎不会将事件从子级转发给父级。考虑这个例子:

Import-Module ShowUI
$wj = Start-WPFJob -ScriptBlock {
    Register-EngineEvent -SourceIdentifier MySource -Forward

    New-Button "Button" -On_Click { 
        New-Event -SourceIdentifier MySource -MessageData 'MyMessage'
        }
}

Wait-Event | select SourceIdentifier, MessageData | Format-List

运行这个例子会产生这个窗口:

但是,单击该按钮不会在父作业中产生事件。


  1. 为什么Start-WPFJob 示例不向父作业生成事件?
  2. 是否有其他方法可以使用 ShowUI 以非阻塞方式生成按钮并从中接收事件?

【问题讨论】:

  • 哦,这是个好问题。或者想法。我的意思是,如果 WPFJob 转发事件会很好,我想知道需要什么......
  • 从 WPFJob 转发事件将为 ShowUI 开辟整个用例世界。 MVC 应用程序只需几行代码就可以实现。
  • 是的,我在玩它。我不确定你为什么要这样做。我的意思是,为什么您希望 UI 中的事件进入不同的流程?您可以流式输出...
  • 我不确定您所说的流输出是什么意思。我有一个管理长时间运行的作业的状态机。状态机由源自 .NET 框架的各种 powershell 事件触发。状态机需要自己的线程,因为它会阻塞等待事件。 UI 需要自己的线程,因为它阻塞了 WPF 中的某个位置。例如,为了让用户暂停/恢复状态机,事件需要从 UI 发送到状态机运行空间……不知何故。
  • 我无法让它工作,我将不得不寻求一些帮助,但要等到新年之后;)我将在下面发布一个糟糕的解决方法......跨度>

标签: wpf events powershell user-interface jobs


【解决方案1】:

到目前为止,我无法让 engineevents 正确转发(实际上,据我所知,我什至无法让他们做任何事情),我认为你最好的选择是运行 WPFJob,而不是New-Event,更新 $Window UIValue,然后从你的主运行空间,而不是 Wait-Event,在循环中使用 Update-WPFJob。

我会将此功能添加到模块中(实际上,我会在源代码管理中但尚未发布的 1.5 版本中添加它):

function Add-UIValue {
    param(
        [Parameter(ValueFromPipeline=$true)]
        [Windows.FrameworkElement]    
        $Ui,

        [PSObject]
        $Value
    )
    process {
        if ($psBoundParameters.ContainsKey('Value')) {
            Set-UIValue $UI (@(Get-UIValue $UI -IgnoreChildControls) + @($Value))
        } else {
            Set-UIValue -Ui $ui 
        }        
    }   
}

然后,像这样:

$job = Start-WPFJob {
  Window {
    Grid -Rows "1*", "Auto" {
      New-ListBox -Row 0 -Name LB -Items (Get-ChildItem ~ -dir)
      Button "Send" -Row 1 -On_Click { Add-UIValue $Window $LB.SelectedItem }
    }
  } -SizeToContent "Width" -MinHeight 800
}

每次单击时,会将所选项目添加到 UI 输出(如果您在没有作业的情况下运行该窗口并单击按钮几次,然后关闭窗口,您将得到两个输出)。

然后你可以在主机中做这样的事情而不是等待事件:

do {
  Update-WPFJob -Job $job -Command { Get-UIValue $Window -IgnoreChildControls } -OutVariable Output
  Start-Sleep -Mil 300
} while (!$Output)

【讨论】:

  • 好的,我想我终于明白你在做什么了。这的一种解决方法,但我认为我可以将大部分丑陋封装起来。您能否为我澄清这一点:在运行$job = Start-WPFJob ... 之后,我是否应该期待do { ... } while ... 循环事件的结果而不关闭窗口?
  • Update-WPFJob 命令将输出Get-UIValue 的结果(并将它们存储在$Output 中,这将停止循环)。然后你就可以用 $Output...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多