我開始構建一個帶有“復制”按鈕和此按鈕下的標簽的小型 winform。當我單擊“復制”按鈕時,它開始將檔案從源復制到目標。我想異步運行它,所以我不希望在復制操作運行時凍結表單。這就是我使用 Job 的原因。成功復制后,我需要復制反饋并顯示帶有綠色的“OK”文本,但它不起作用。
這是我的代碼:
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.Application]::EnableVisualStyles()
Function Copy-Action{
$Computername = "testclient"
$Source_Path = "C:\temp\"
$Destination_Path = "\\$Computername\c$\temp"
$job = Start-Job -Name "Copy" -ArgumentList $Source_Path,$Destination_Path –ScriptBlock {
param($Source_Path,$Destination_Path)
Copy-Item $Source_Path -Destination $Destination_Path -Recurse -Force
}
Register-ObjectEvent $job StateChanged -MessageData $Status_Label -Action {
[Console]::Beep(1000,500)
$Status_Label.Text = "OK"
$Status_Label.ForeColor = "#009900"
$eventSubscriber | Unregister-Event
$eventSubscriber.Action | Remove-Job
} | Out-Null
}
# DRAW FORM
$form_MainForm = New-Object System.Windows.Forms.Form
$form_MainForm.Text = "Test Copy"
$form_MainForm.Size = New-Object System.Drawing.Size(200,200)
$form_MainForm.FormBorderStyle = "FixedDialog"
$form_MainForm.StartPosition = "CenterScreen"
$form_MainForm.MaximizeBox = $false
$form_MainForm.MinimizeBox = $true
$form_MainForm.ControlBox = $true
# Copy Button
$Copy_Button = New-Object System.Windows.Forms.Button
$Copy_Button.Location = "50,50"
$Copy_Button.Size = "75,30"
$Copy_Button.Text = "Copy"
$Copy_Button.Add_Click({Copy-Action})
$form_MainForm.Controls.Add($Copy_Button)
# Status Label
$Status_Label = New-Object System.Windows.Forms.Label
$Status_Label.Text = ""
$Status_Label.AutoSize = $true
$Status_Label.Location = "75,110"
$Status_Label.ForeColor = "black"
$form_MainForm.Controls.Add($Status_Label)
#show form
$form_MainForm.Add_Shown({$form_MainForm.Activate()})
[void] $form_MainForm.ShowDialog()
復制成功,但不會顯示“OK”標簽。我已經放置了一個 Beep 但它也不起作用。我究竟做錯了什么 ?有什么解決辦法嗎?謝謝你。
uj5u.com熱心網友回復:
Start-Job創建一個單獨的行程,當您的表單準備好接收事件時,它無法偵聽作業事件。您需要創建一個新的運行空間,它能夠同步執行緒和表單控制元件。
我改編了這個答案的代碼。你可以在那里閱讀更好的解釋。
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.Application]::EnableVisualStyles()
Function Copy-Action{
$SyncHash = [hashtable]::Synchronized(@{TextBox = $Status_Label})
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.ThreadOptions = "UseNewThread"
$Runspace.Open()
$Runspace.SessionStateProxy.SetVariable("SyncHash", $SyncHash)
$Worker = [PowerShell]::Create().AddScript({
$SyncHash.TextBox.Text = "Copying..."
# Copy-Item
$Computername = "testclient"
$Source_Path = "C:\temp\"
$Destination_Path = "\\$Computername\c$\temp"
Copy-Item $Source_Path -Destination $Destination_Path -Recurse -Force
$SyncHash.TextBox.ForeColor = "#009900"
$SyncHash.TextBox.Text = "OK"
})
$Worker.Runspace = $Runspace
$Worker.BeginInvoke()
}
# DRAW FORM
$form_MainForm = New-Object System.Windows.Forms.Form
$form_MainForm.Text = "Test Copy"
$form_MainForm.Size = New-Object System.Drawing.Size(200,200)
$form_MainForm.FormBorderStyle = "FixedDialog"
$form_MainForm.StartPosition = "CenterScreen"
$form_MainForm.MaximizeBox = $false
$form_MainForm.MinimizeBox = $true
$form_MainForm.ControlBox = $true
# Copy Button
$Copy_Button = New-Object System.Windows.Forms.Button
$Copy_Button.Location = "50,50"
$Copy_Button.Size = "75,30"
$Copy_Button.Text = "Copy"
$Copy_Button.Add_Click({Copy-Action})
$form_MainForm.Controls.Add($Copy_Button)
# Status Label
$Status_Label = New-Object System.Windows.Forms.Label
$Status_Label.Text = ""
$Status_Label.AutoSize = $true
$Status_Label.Location = "75,110"
$Status_Label.ForeColor = "black"
$form_MainForm.Controls.Add($Status_Label)
#show form
$form_MainForm.Add_Shown({$form_MainForm.Activate()})
[void] $form_MainForm.ShowDialog()
uj5u.com熱心網友回復:
讓我提供balrundel 有用的解決方案的替代方案- 這是有效但復雜的。
核心問題是,當表單以模態方式顯示時.ShowDialog(),WinForms 控制著前臺執行緒,而不是 PowerShell。
也就是說,PowerShell 代碼 - 在表單的事件處理程式中 - 僅回應用戶操作而執行,這就是為什么傳遞給Register-ObjectEvent's-Action引數的作業狀態更改事件處理程式不會觸發(它最終會在關閉表單后觸發) .
有兩個基本的解決方案:
.ShowDialog()在不同的 PowerShell 運行空間(執行緒)中堅持并并行執行操作。balrundel 的解決方案使用PowerShell SDK來實作這一點,不幸的是,它的使用絕非易事。
請參閱下面的基于的更簡單的替代方案
Start-ThreadJob
通過方法非模態地顯示表單
.Show(),并進入一個回圈,您可以在其中執行其他操作,同時定期呼叫[System.Windows.Forms.Application]::DoEvents()以保持表單回應。有關此技術的示例,請參閱此答案。
一種混合方法是堅持
.ShowDialog()并在表單事件處理程式中進入一個[System.Windows.Forms.Application]::DoEvents()回圈。- 這最好僅限于應用此技術的單個事件處理程式,因為使用額外的同時
[System.Windows.Forms.Application]::DoEvents()回圈會帶來麻煩。 - 有關此技術的示例,請參閱此答案。
- 這最好僅限于應用此技術的單個事件處理程式,因為使用額外的同時
更簡單的Start-ThreadJob基于解決方案:
Start-ThreadJob是ThreadJob模塊的一部分,它為基于子行程的常規后臺作業提供輕量級、基于執行緒的替代方案,也是通過 PowerShell SDK 創建運行空間的更方便的替代方案。- 它隨PowerShell (Core) 7 一起提供,可以按需安裝在Windows PowerShell中,例如
Install-Module ThreadJob -Scope CurrentUser. - 在大多數情況下,執行緒作業是更好的選擇,無論是性能還是型別保真度 - 請參閱此答案的底部以了解原因。
- 它隨PowerShell (Core) 7 一起提供,可以按需安裝在Windows PowerShell中,例如
除了語法上的便利之外
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
uj5u.com熱心網友回復:
這可能與代碼塊的順序有關。
標簽文本設定為“OK”后,被初始化陳述句覆寫
$Status_Label.Text = ""
并且可能因此沒有顯示更新的值。嘗試將函式放在腳本的底部,或者至少將標簽塊放在設定標簽文本的塊上方,例如:
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.Application]::EnableVisualStyles()
Function Copy-Action{
$Computername = "testclient"
$Source_Path = "C:\temp\"
$Destination_Path = "\\$Computername\c$\temp"
$job = Start-Job -Name "Copy" -ArgumentList $Source_Path,$Destination_Path –ScriptBlock {
param($Source_Path,$Destination_Path)
Copy-Item $Source_Path -Destination $Destination_Path -Recurse -Force
}
Register-ObjectEvent $job StateChanged -MessageData $Status_Label -Action {
[Console]::Beep(1000,500)
$Status_Label.Text = "OK"
$Status_Label.ForeColor = "#009900"
$eventSubscriber | Unregister-Event
$eventSubscriber.Action | Remove-Job
} | Out-Null
}
# DRAW FORM
$form_MainForm = New-Object System.Windows.Forms.Form
$form_MainForm.Text = "Test Copy"
$form_MainForm.Size = New-Object System.Drawing.Size(200,200)
$form_MainForm.FormBorderStyle = "FixedDialog"
$form_MainForm.StartPosition = "CenterScreen"
$form_MainForm.MaximizeBox = $false
$form_MainForm.MinimizeBox = $true
$form_MainForm.ControlBox = $true
# Status Label
$Status_Label = New-Object System.Windows.Forms.Label
$Status_Label.Text = ""
$Status_Label.AutoSize = $true
$Status_Label.Location = "75,110"
$Status_Label.ForeColor = "black"
$form_MainForm.Controls.Add($Status_Label)
# Copy Button
$Copy_Button = New-Object System.Windows.Forms.Button
$Copy_Button.Location = "50,50"
$Copy_Button.Size = "75,30"
$Copy_Button.Text = "Copy"
$Copy_Button.Add_Click({Copy-Action})
$form_MainForm.Controls.Add($Copy_Button)
#show form
$form_MainForm.Add_Shown({$form_MainForm.Activate()})
[void] $form_MainForm.ShowDialog()
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/357989.html
上一篇:如何在valuechanged事件之前獲取NumericUpDown的文本?
下一篇:是否可以動態調整控制元件的大小?



