Tec*_*hno 2 powershell winforms
我开始构建一个带有“复制”按钮和该按钮下的标签的小型 winform。\n当我单击“复制”按钮时,它开始将文件从源复制到目标。\n我想异步运行此操作,因此我不希望表单复制操作运行时被冻结。这就是我使用 Job 的原因。成功复制后,我需要复制反馈并显示绿色的“OK”文本,但它不起作用。
\n这是我的代码:
\n[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")\n[System.Windows.Forms.Application]::EnableVisualStyles()\n\nFunction Copy-Action{\n\n $Computername = "testclient"\n\n $Source_Path = "C:\\temp\\"\n $Destination_Path = "\\\\$Computername\\c$\\temp"\n\n\n $job = Start-Job -Name "Copy" -ArgumentList $Source_Path,$Destination_Path \xe2\x80\x93ScriptBlock {\n param($Source_Path,$Destination_Path) \n \n Copy-Item $Source_Path -Destination $Destination_Path -Recurse -Force\n \n } \n \n Register-ObjectEvent $job StateChanged -MessageData $Status_Label -Action {\n [Console]::Beep(1000,500)\n $Status_Label.Text = "OK"\n $Status_Label.ForeColor = "#009900"\n $eventSubscriber | Unregister-Event\n $eventSubscriber.Action | Remove-Job\n } | Out-Null\n}\n\n# DRAW FORM\n$form_MainForm = New-Object System.Windows.Forms.Form\n$form_MainForm.Text = "Test Copy"\n$form_MainForm.Size = New-Object System.Drawing.Size(200,200)\n$form_MainForm.FormBorderStyle = "FixedDialog"\n$form_MainForm.StartPosition = "CenterScreen"\n$form_MainForm.MaximizeBox = $false\n$form_MainForm.MinimizeBox = $true\n$form_MainForm.ControlBox = $true\n\n# Copy Button\n$Copy_Button = New-Object System.Windows.Forms.Button\n$Copy_Button.Location = "50,50"\n$Copy_Button.Size = "75,30"\n$Copy_Button.Text = "Copy"\n$Copy_Button.Add_Click({Copy-Action})\n$form_MainForm.Controls.Add($Copy_Button)\n\n# Status Label\n$Status_Label = New-Object System.Windows.Forms.Label\n$Status_Label.Text = ""\n$Status_Label.AutoSize = $true\n$Status_Label.Location = "75,110"\n$Status_Label.ForeColor = "black"\n$form_MainForm.Controls.Add($Status_Label)\n\n#show form\n$form_MainForm.Add_Shown({$form_MainForm.Activate()})\n[void] $form_MainForm.ShowDialog()\nRun Code Online (Sandbox Code Playgroud)\n复制成功,但不会显示“OK”标签。我已经设置了蜂鸣声,但它也不起作用。\n我做错了什么?有什么解决办法吗?\n谢谢。
\n让我提供balrundel 有用的解决方案的替代方案- 这是有效的,但很复杂。
核心问题是,当表单以模态方式显示时,.ShowDialog()WinForms 控制着前台线程,而不是 PowerShell。
也就是说,PowerShell 代码 - 在表单的事件处理程序中 - 仅响应用户操作Register-ObjectEvent而执行,这就是为什么传递给的参数的作业状态更改事件处理-Action程序不会触发(它最终会在关闭表单后触发) 。
有两个基本解决方案:
坚持.ShowDialog()在不同的 PowerShell 运行空间(线程)中并行执行操作。
balrundel 的解决方案使用PowerShell SDK来实现这一点,不幸的是,它的使用绝非微不足道。
请参阅下面的更简单的替代方案Start-ThreadJob
通过方法以非模式方式显示表单.Show(),并进入一个循环,您可以在其中执行其他操作,同时定期调用[System.Windows.Forms.Application]::DoEvents()以保持表单响应。
更简单的Start-ThreadJob基于 - 的解决方案:
Start-ThreadJob是该模块的一部分,该ThreadJob模块为基于子进程的常规后台作业提供了一种轻量级的、基于线程的替代方案,并且也是通过 PowerShell SDK 创建运行空间的更方便的替代方案。
Install-Module ThreadJob -Scope CurrentUser.除了语法上的便利之外,Start-ThreadJob由于它是基于线程的(而不是使用子进程,这就是Start-Job它的作用),因此允许操作调用线程的活动对象。
以下简化的独立示例代码演示了该技术:
该示例显示了一个带有按钮的简单表单,该按钮启动线程作业,并在操作(通过 3 秒睡眠模拟)完成后从该线程作业内部更新表单,如以下屏幕截图所示:

Start Job(表单保持响应):


事件处理程序.add_Click()包含解决方案的核心内容;希望源代码注释能够提供足够的文档。
# PSv5+
using namespace System.Windows.Forms
using namespace System.Drawing
Add-Type -AssemblyName System.Windows.Forms
# Create a sample form.
$form = [Form] @{
Text = 'Form with Thread Job'
ClientSize = [Point]::new(200, 80)
FormBorderStyle = 'FixedToolWindow'
}
# Create the controls and add them to the form.
$form.Controls.AddRange(@(
($btnStartJob = [Button] @{
Text = "Start Job"
Location = [Point]::new(10, 10)
})
[Label] @{
Text = "Status:"
AutoSize = $true
Location = [Point]::new(10, 40)
Font = [Font]::new('Microsoft Sans Serif', 10)
}
($lblStatus = [Label] @{
Text = "(Not started)"
AutoSize = $true
Location = [Point]::new(80, 40)
Font = [Font]::new('Microsoft Sans Serif', 10)
})
))
# The script-level helper variable that maintains a collection of
# thread-job objects created in event-handler script blocks,
# which must be cleaned up after the form closes.
$script:jobs = @()
# Add an event handler to the button that starts
# the background job.
$btnStartJob.add_Click( {
$this.Enabled = $false # To prevent re-entry while the job is still running.
# Signal the status.
$lblStatus.Text = 'Running...'
$form.Refresh() # Update the UI.
# Start the thread job, and add the job-info object to
# the *script-level* $jobs collection.
# The sample job simply sleeps for 3 seconds to simulate a long-running operation.
# Note:
# * The $using: scope is required to access objects in the caller's thread.
# * In this simple case you don't need to maintain a *collection* of jobs -
# you could simply discard the previous job, if any, and start a new one,
# so that only one job object is ever maintained.
$script:jobs += Start-ThreadJob {
# Perform the long-running operation.
Start-Sleep -Seconds 3
# Update the status label and re-enable the button.
($using:lblStatus).Text = 'Done'
($using:btnStartJob).Enabled = $true
}
})
$form.ShowDialog()
# Clean up the collection of jobs.
$script:jobs | Remove-Job -Force
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2500 次 |
| 最近记录: |