如何在PowerShell WinForm表格Overview列实现多进程带百分比进度条
PowerShell WinForm DataGridView 多行进度条实现方案
要实现每行独立的带百分比进度条,核心是自定义DataGridViewProgressColumn列类型,配合后台线程处理任务并安全更新UI,避免阻塞界面。以下是完整可运行的代码:
1. 定义自定义进度列
创建继承自DataGridViewImageColumn的自定义列,用于绘制带百分比的进度条:
Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing # 自定义进度列:绘制进度条+百分比文本 class DataGridViewProgressColumn : System.Windows.Forms.DataGridViewImageColumn { DataGridViewProgressColumn() { this.CellTemplate = [DataGridViewProgressCell]::new() this.Width = 150 } } class DataGridViewProgressCell : System.Windows.Forms.DataGridViewImageCell { hidden [int]$progressValue = 0 DataGridViewProgressCell() { this.ValueType = [int] } # 重写绘制方法,绘制进度条和百分比 hidden [void] Paint([System.Drawing.Graphics]$g, [System.Drawing.Rectangle]$clipBounds, [System.Drawing.Rectangle]$cellBounds, [int]$rowIndex, [System.Windows.Forms.DataGridViewElementStates]$cellState, [object]$value, [object]$formattedValue, [string]$errorText, [System.Windows.Forms.DataGridViewCellStyle]$cellStyle, [System.Windows.Forms.DataGridViewAdvancedBorderStyle]$advancedBorderStyle, [System.Windows.Forms.DataGridViewPaintParts]$paintParts) { if ($value -is [int]) { $this.progressValue = $value } # 绘制单元格背景 $g.FillRectangle([System.Drawing.SolidBrush]$cellStyle.BackColor, $cellBounds) # 绘制进度条背景 $progressBarBounds = [System.Drawing.Rectangle]::new($cellBounds.X + 2, $cellBounds.Y + 2, $cellBounds.Width - 4, $cellBounds.Height - 4) $g.DrawRectangle([System.Drawing.Pens]::Gray, $progressBarBounds) # 绘制进度条填充 if ($this.progressValue -gt 0) { $fillWidth = [int]($progressBarBounds.Width * ($this.progressValue / 100.0)) $g.FillRectangle([System.Drawing.SolidBrush][System.Drawing.Color]::LightGreen, $progressBarBounds.X, $progressBarBounds.Y, $fillWidth, $progressBarBounds.Height) } # 绘制百分比文本 $text = "$($this.progressValue)%" $textSize = $g.MeasureString($text, $cellStyle.Font) $textX = $cellBounds.X + ($cellBounds.Width - $textSize.Width) / 2 $textY = $cellBounds.Y + ($cellBounds.Height - $textSize.Height) / 2 $g.DrawString($text, $cellStyle.Font, [System.Drawing.Brushes]::Black, $textX, $textY) } }
2. 初始化GUI并启动后台任务
创建Form和DataGridView,添加自定义列,用后台线程执行任务并更新对应行的进度:
# 创建主窗体 $form = [System.Windows.Forms.Form]::new() $form.Text = "多任务进度监控" $form.Size = [System.Drawing.Size]::new(600, 400) # 创建DataGridView $dgv = [System.Windows.Forms.DataGridView]::new() $dgv.Dock = [System.Windows.Forms.DockStyle]::Fill $dgv.AutoGenerateColumns = $false # 添加列 $dgv.Columns.Add(([System.Windows.Forms.DataGridViewTextBoxColumn]::new() { Name = "TaskName" HeaderText = "任务名称" Width = 200 })) $dgv.Columns.Add(([DataGridViewProgressColumn]::new() { Name = "Overview" HeaderText = "进度" })) # 添加任务行 $tasks = @( @{ Name = "文件备份"; Progress = 0 }, @{ Name = "数据库同步"; Progress = 0 }, @{ Name = "日志清理"; Progress = 0 } ) foreach ($task in $tasks) { $rowIndex = $dgv.Rows.Add($task.Name, 0) $dgv.Rows[$rowIndex].Tag = $task # 存储任务对象用于后续更新 } $form.Controls.Add($dgv) # 启动后台线程处理任务,避免阻塞UI $backgroundWorker = [System.ComponentModel.BackgroundWorker]::new() $backgroundWorker.WorkerReportsProgress = $true $backgroundWorker.DoWork += { param($sender, $e) # 模拟三个任务依次执行 foreach ($row in $dgv.Rows) { $task = $row.Tag for ($i = 0; $i -le 100; $i += 10) { Start-Sleep -Milliseconds 200 # 模拟任务耗时 # 报告进度:行索引 + 当前进度值 $sender.ReportProgress($row.Index, $i) } } } # 处理进度更新事件,跨线程更新UI $backgroundWorker.ProgressChanged += { param($sender, $e) $rowIndex = $e.ProgressPercentage $progressValue = $e.UserState # 用Invoke确保在UI线程更新控件 $dgv.Invoke({ $dgv.Rows[$rowIndex].Cells["Overview"].Value = $progressValue $dgv.Refresh() # 刷新单元格触发重绘 }) } # 任务完成后提示 $backgroundWorker.RunWorkerCompleted += { [System.Windows.Forms.MessageBox]::Show("所有任务完成!") } # 窗体加载时启动后台任务 $form.Load += { $backgroundWorker.RunWorkerAsync() } # 显示窗体 $form.ShowDialog() | Out-Null
关键说明
- 自定义列重绘:通过重写
Paint方法,手动绘制进度条和百分比文本,实现带文字的进度显示。 - 后台线程:使用
BackgroundWorker执行耗时任务,避免UI线程阻塞,保证窗体启动后立即响应。 - 跨线程UI更新:WinForm控件只能在创建它的线程操作,所以用
Invoke方法将更新操作切换到UI线程执行。 - 行关联任务:通过
Row.Tag存储任务对象,方便后台线程找到对应行更新进度。
内容的提问来源于stack exchange,提问作者Cheries
相关产品推荐
相关产品推荐

