我创建了一个Windows窗体,您可以在其中单击一个按钮来启动备份过程(使用Start-Job
)大约15分钟。我使用Start-Job
是为了在备份过程中保持表单的响应性(所谓响应是指可以移动、最小化等等)。然而,我希望表单在作业完成后弹出一个消息框,但我无法获得正确的结果。
一开始我尝试了一个While
循环,每10秒检查一次任务是否完成:
$BackupButton.Add_Click( {
$BackupJob = Start-Job -ScriptBlock { ... }
$Completed = $false
while (!($Completed)) {
if ($BackupJob.State -ne "Running") {
$Completed = $true
}
Start-Sleep -Seconds 10
}
[System.Windows.Forms.MessageBox]::Show('Successfully completed the backup process.', 'Backup Tool', 'OK', 'Info')
})
这在作业完成后给了我一个消息框,但表单在进程中没有响应,可能是因为它仍在为While
循环使用线程的资源。
然后,我尝试使用Register-ObjectEvent
调用消息框,以显示作业状态何时发生更改:
$BackupButton.Add_Click( {
$BackupJob = Start-Job -ScriptBlock { ... }
Register-ObjectEvent $BackupJob StateChanged -Action {
[System.Windows.Forms.MessageBox]::Show('Successfully completed the backup process.', 'Backup Tool', 'OK', 'Info')
}
})
这个选项确实可以让表单在行程期间保持回应,但是消息方块(事件的动作区块)永远不会启动,直到我关闭Windows表单为止。
是否有任何选项既可以使消息框按时显示(不在表单关闭时显示)又可以不使用表单的线程(保持其响应性)?
**编辑:**或者,有没有办法从后台作业控制窗体?我尝试将窗体的按钮/控件作为参数发送给作业,然后从作业控制窗体的事件,但没有成功。如果有办法从后台作业访问窗体,也可以解决我的问题。
先谢谢你。
2条答案
按热度按时间5t7ly7z51#
Start-Sleep
指令程式会使您的表单没有回应。若要克服这个问题,请改用System.Windows.Forms.Timer
对象。类似于:
希望能有所帮助
tzdcorbm2#
Theo's helpful answer解释了您的方法中存在的问题,并给出了一个有效的解决方案(您自己对此进行了改进,注意到实际上并不需要使用
[System.Windows.Forms.Timer]
示例)。如图所示,为了在执行其他任务的同时保持窗体的响应性,必须在循环中不断调用
[System.Windows.Forms.Application]::DoEvents()
,并且该循环体必须快速执行;换句话说就是:您只能在循环内执行类似 * 轮询 * 的活动。[System.Windows.Forms.Application]::DoEvents()
通常会有问题(这基本上是阻塞.ShowDialog()
调用在后台所做的),但在这种受限的场景中(假设只显示 * 一个 * 表单),它应该是好的。请参见this answer了解背景信息。从 * 给定控件的事件处理程序 * 内部运行
DoEvents()
循环的另一种方法是 * 非 -模态地-使用.Show()
而不是.ShowDialog()
-显示表单,然后将DoEvents()
循环 * 直接放在 * 此调用之后, 在脚本的主作用域 * 中。这种方法的优点是:
DoEvents
循环,将其放置在主脚本作用域中也有助于使整个控制流更加清晰。下面是一个自包含的示例(需要PowerShell版本5或更高版本);它将创建一个表单,该表单具有一个
Start Job
按钮和一个反映作业状态的状态标签:Start Job
后;表单在此状态下仍然有响应-例如,您可以四处移动它,如果有其他(启用的)控件,您可以单击它们):有关此解决方案的变体(持续收集作业的 * 输出并将其显示在多行文本框中),请参见this answer。