如何在PowerShell中清空并复用Windows Forms的DataGridView?
解决DataGridView重复加载数据时的红叉白屏问题
这个问题的核心原因有两个:一是后台线程直接操作UI控件违反了WinForms的线程安全规则,二是没有正确重置数据源并在主线程中更新UI。WinForms控件只能由创建它们的线程(也就是主线程)修改,后台线程直接赋值DataSource会导致控件状态混乱,第二次点击就触发了错误。
下面是修复后的完整代码,我会标注关键修改点:
Add-Type -AssemblyName System.Windows.Forms $sync = [Hashtable]::Synchronized(@{}) $backgroundTask = { # 先通过Invoke让主线程清空DataGridView $sync.softwareTable.Invoke([Action]{ $sync.softwareTable.DataSource = $null $sync.softwareTable.Rows.Clear() }) $task1 = [PowerShell]::Create().AddScript({ param($sync) # 执行数据查询(推荐用Get-CimInstance替代已弃用的Get-WmiObject) $softwareQuery = Get-CimInstance -ComputerName localhost -ClassName Win32_Product | Select-Object Name $softwareList = New-Object System.Collections.ArrayList $softwareList.AddRange($softwareQuery) # 必须通过Invoke回到主线程更新UI $sync.softwareTable.Invoke([Action]{ $sync.softwareTable.DataSource = $softwareList }) }).AddArgument($sync) $runspace = [RunspaceFactory]::CreateRunspace() $runspace.ApartmentState = "STA" $runspace.ThreadOptions = "ReuseThread" $runspace.Open() $runspace.SessionStateProxy.SetVariable("sync", $sync) $task1.Runspace = $runspace $asyncResult = $task1.BeginInvoke() # 清理资源,避免内存泄漏 $task1.EndInvoke($asyncResult) $task1.Dispose() $runspace.Close() $runspace.Dispose() } $mainForm = New-Object system.Windows.Forms.Form $mainForm.Size = New-Object System.Drawing.Size(900,600) $okButton = New-Object System.Windows.Forms.Button $okButton.Location = New-Object System.Drawing.Point(280,5) $okButton.Size = New-Object System.Drawing.Size(75,20) $okButton.Text = "OK" $okButton.Add_Click($backgroundTask) ; $mainForm.Controls.Add($okButton) $softwareTable = New-Object System.Windows.Forms.DataGridView -Property @{ Location=New-Object System.Drawing.Point(5,30) Size=New-Object System.Drawing.Size(840,360) ColumnHeadersVisible = $true } $mainForm.Controls.Add($softwareTable) $sync.softwareTable = $softwareTable $mainForm.ShowDialog()
关键修改说明:
- 线程安全的UI更新:所有对
DataGridView的操作(清空、赋值数据源)都通过Invoke方法委托给主线程执行,这是WinForms线程安全的核心要求,避免跨线程操作导致的控件状态崩溃。 - 彻底清空数据源:每次点击按钮时,先在主线程中将
DataSource设为null并清空行,确保之前的数据被完全重置,不会和新数据产生冲突。 - 资源清理:添加了
EndInvoke、Dispose方法来清理PowerShell实例和Runspace,避免重复点击后出现内存泄漏问题。 - 替换弃用命令:把
Get-WmiObject换成了官方推荐的Get-CimInstance,兼容性和性能更好。
这样修改后,你可以重复点击OK按钮,DataGridView会正确清空并重新加载数据,不会再出现红叉白屏的问题。
内容的提问来源于stack exchange,提问作者jemandanders
相关产品推荐
相关产品推荐

