VB.net 2010 backgroundworker更新表单progressbar

时间:2012-08-01 02:54:01

标签: .net vb.net winforms progress-bar backgroundworker

试图了解背景工作者:)

Imports System.Threading
Imports System
Imports System.IO
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Text.RegularExpressions
Imports System.Text
Imports System.Diagnostics
Imports System.Drawing


Public Class CLViewForm

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    BackgroundWorker1.RunWorkerAsync()
End Sub


Function SystemLoads()
    Dim PCV As Integer = Convert.ToDecimal(PerformanceCounter1.NextValue())
    Me.Label5.Text = PCV & " %"
    Me.ProgressBar1.Minimum = 0
    Me.ProgressBar1.Maximum = 100
    Me.ProgressBar1.Step = 1
    Me.ProgressBar1.Value = PCV
    Me.Label6.Text = Now.ToLongTimeString
    Return 0
End Function

Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    Me.Invoke(SystemLoads())
    End Sub
End Class

我想要做的基本要点就是加载一个后台工作程序来更新主表单上的3件事 显示当前系统负载的进度条 一个文本标签,以百分比显示负载 和一个时钟

我知道我可以用一个简单的计时器完成所有这些,但是当我去添加我的其他计时器时,它往往会使表格变得迟钝所以我希望学习如何拍摄我想做的大部分事情进入后台线程。

我对这一切都是新手,所以我不知道如何调用这些东西并保持线程安全,因此我得到一个错误[当然,这就是为什么我在这里问:)] “vb.net跨线程操作无效控制'progressbar1'从一个线程访问,而不是它被称为”

或者那种效果。

使用我提供的代码示例如何让后台线程更新表单的进度条和%标签?

TIA

6 个答案:

答案 0 :(得分:1)

我试图在这里创建一个简单的例子。通过使用委托你是在正确的路线,但我认为你的实施有点偏。

您需要具有labelprogressbarbackgroundworker控件的表单,然后将以下代码放入:

Private Sub TestForm_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    'Start the worker
    BackgroundWorker1.RunWorkerAsync()
End Sub

Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    'simulate long running processes
    UpdateStatus(0, "Loading")
    System.Threading.Thread.Sleep(1000)
    UpdateStatus(33, "One third of the way through")
    System.Threading.Thread.Sleep(1000)
    UpdateStatus(66, "Two thirds of the way through")
    System.Threading.Thread.Sleep(1000)
    UpdateStatus(100, "Finished")
End Sub

'all calls to update the progress bar and label go through here
Private Sub UpdateStatus(ByVal progress As Integer, ByVal status As String)
    Try
        If Me.InvokeRequired Then
            Dim cb As New UpdateStatusCallback(AddressOf UpdateStatusDelegate)
            Me.Invoke(cb, New Object() {progress, status})
        Else
            UpdateStatusDelegate(progress, status)
        End If
    Catch ex As Exception
        MessageBox.Show("There was an error " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End Try
End Sub

Private Delegate Sub UpdateStatusCallback(ByVal progress As Integer, ByVal status As String)

'This catually updates the control - modify the paramters and update to suit the control you are using
Private Sub UpdateStatusDelegate(ByVal progress As Integer, ByVal status As String)
    ProgressBar1.Value = progress
    Label1.Text = status
End Sub

答案 1 :(得分:0)

为后台工作程序RunWorkerCompleted和ProgressChanged创建一个事件。
C#

this.backgroundWorker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(/* the event method */);
this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(/* the event method */);

另外,不要忘记将WorkerReportsProgress设置为true ..

答案 2 :(得分:0)

您需要设置:

Backgroundworker可以使用进度条

Backgroundworker可以报告进度

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
     BW1.RunAsync()
End Sub

然后:

Private Sub BW1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BW1.DoWork

    Dim i as integer = 1
    Do Until integer = 0

        IF BW1.CancellationPending = True then
             MsgBox("Cancelled by user")
             Exit Sub
        End If


         ....


        BW1.ReportProgress(i)

    Loop

End Sub

显示进度:

Private Sub BW1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BW1.ProgressChanged
     Progressbar1.Value = e.ProgressPercentage  
End Sub

编辑:

您无法直接在DoWork方法上使用UI控件。您需要先委托它们,但要显示进度(进度条,标签等)。后台工作者拥有它允许您以安全模式使用UI的方法(ProgressChanged())。

答案 3 :(得分:0)

进度条有点愚蠢和令人困惑。我不得不求助于以相同的形式保留我的所有工作(如果可以的话)。因此,在您的情况下,我可能会尝试以相同的形式在过程中执行系统负载。这对我有用。 我希望它有所帮助。我已经粘贴了一些可能解释它的代码(不能让它在SO中格式化 - 对不起)但简而言之:

  1. 我从Me.BackgroundWorker.RunWorkerAsync()开始 Do-work活动。
  2. 调用BackgroundWorker_DoWork(调用 你想要做的工作并报告回来)
  3. 完成工作并将使用BackgroundWorker.ReportProgress
  4. 报告的Proc

    <强>声明

    Public Event DoWork As DoWorkEventHandler
    Public Event PrgRprt As ProgressChangedEventHandler
    Public g_intProgress As Integer             
    

    进度条信息

     Private Sub btnStepOne_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStepTwo.Click
        'Calls  BackgroundWorker_DoWork, which in turn calls step One
        Me.BackgroundWorker1.RunWorkerAsync()
    End Sub
    
    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker2.DoWork
        'Called from Me.BackgroundWorker1.RunWorkerAsync()
        'calls step One proc, where the work is done
        Call StepOne(strInput)
    End Sub
    
    Private Function StepOne(strInput as string)as string
       'this is where I do my work that the progress bar will monitor   
        Dim x as integer    
        For x = 0 to 100
         'Do your calculations as you loop
          'This is where you report back to the worker to change progress bar   
            Me.BackgroundWorker1.ReportProgress(x)
        Next x
    
    End function
    
    
    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker2.ProgressChanged
    
        Me.pbConvertion.Value = e.ProgressPercentage
        Me.g_intProgress = e.ProgressPercentage
        Me.pbConvertion.Maximum = "100"
        Me.pbConvertion.Update()
        Me.lblProgbar.Text = "Percentage - " & Me.pbConvertion.Value & "% Completed"
    End Sub
    
    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        Me.lblProgbar.Text = "Step One Completed"
    End Sub
    

答案 4 :(得分:0)

我使用的技巧是可怕的,但它有效:

            pgbMain.Maximum += 1
            pgbMain.Value += 2
            pgbMain.Value -= 1
            pgbMain.Maximum -= 1

或者您可以使用:

            i = pgbMain.Value + 1
            pgbMain.Value = pgbMain.Maximum
            pgbMain.Value = i

但后一种方法意味着最后一步与往常一样延迟,因为没有“后退”步骤。 (不,这两种方法都不会在视觉上检测到。)

当手动调整进度条Value属性时,您可以在WinForm上看到它逐渐移动到更高的值但立即移动到更低的值。所以这似乎摆脱了滞后。不幸的是

答案 5 :(得分:0)

为您提供了更好的解决方案,但以下是我用来改变您的想法的更改:

  1. 正如您在评论中提到的那样,BackgroundWorker仅执行一次DoWork事件。因此,请在RunWorkerAsync事件中再次致电RunWorkerCompleted
  2. VB.NET通常在您使用AddressOf时提供委托,但Control.Invoke过于笼统,因此您必须指定一个。但是,框架提供了MethodInvoker。因此,请将SystemLoads更改为Sub
  3. 一些&#34;百分比&#34; PerfomaceCounters似乎没有遵守0<=%<=100
  4. 因此,让您通过当前障碍的简单更改可以为您提供:

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        BackgroundWorker1.RunWorkerAsync()
    End Sub
    
    Sub SystemLoads()
        Dim PCV As Integer = Convert.ToInt32(PerformanceCounter1.NextValue())
        Me.Label5.Text = PCV & " %"
        Me.ProgressBar1.Minimum = 0
        Me.ProgressBar1.Maximum = 100
        Me.ProgressBar1.Step = 1
        Me.ProgressBar1.Value = Math.Min(Math.Max(0, PCV), 100)
        Me.Label6.Text = Now.ToLongTimeString
    End Sub
    
    Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Me.Invoke(New Methodinvoker(AddressOf SystemLoads))
    End Sub
    
    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        BackgroundWorker1.RunWorkerAsync()
    End Sub
    

    这导致了我在LinqPad query中解决的一些其他问题,具体来说:

    1. 关闭窗口时始终获得ObjectDisposedException。我尝试过缓解它,但是,最后,我在Form被处置时忽略了它。
    2. 以其他方式迎合例外情况。
    3. Sleep稍微减少更新,以减少调整表单大小时的迟缓。这是显示Timer会更好的地方。
    4. 使用正确的If InvokeRequired模式,即使此处不需要。
    5. NB我必须制作自己的控制位置,而且它们现在几乎没有问题。 此外,您必须将PerformanceCounter更改为系统中至少存在InstanceName