如何在不杀死ProgressBar的情况下等待BackgroundWorker完成?

时间:2016-06-19 01:49:21

标签: vb.net progress-bar backgroundworker

该应用程序的功能远不止这些,但我已通过以下示例缩小了问题范围。

当bgwDone.WaitOne()被注释掉时,进度条工作正常,取消按钮有效,但在后台进程完成之前继续执行。

当应用bgwDone.WaitOne()时,ProgressForm可见但未启用,因此无法取消处理并且不会刷新进度条,而且最令人困惑的部分是Msgbox(&#34; 1&#34;)< em>不执行。我只在后台工作程序完成后才看到Msgbox(&#34; 2&#34;)。我完全感到困惑。

Imports System.ComponentModel

Public Class Form1
    Private WithEvents bgw As BackgroundWorker
    Private Event bgwCancelled()
    Private bgwDone As New System.Threading.AutoResetEvent(False)

    'Allows ProgressForm to cancel execution
    Public Sub bgwCancelAsync()
        RaiseEvent bgwCancelled()
    End Sub

    Private Sub bgw_Cancelled_by_ProgressForm() Handles Me.bgwCancelled
        bgw.CancelAsync()
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Cursor = Cursors.WaitCursor
        bgw = New BackgroundWorker
        bgw.WorkerReportsProgress = True
        bgw.WorkerSupportsCancellation = True
        If bgw.IsBusy = False Then
            ProgressForm.Show()
            bgw.RunWorkerAsync(10)
        End If

        '********THIS LINE: bgwDone.WaitOne() MAKES A BIG DIFFERENCE*******
        bgwDone.WaitOne()
        MsgBox("1")
        MsgBox("2")
        Cursor = Cursors.Default
    End Sub

    'BackgroundWorker.RunWorkerAsync raises the DoWork event
    Private Sub bgw_DoWork(sender As Object, e As DoWorkEventArgs) Handles bgw.DoWork
        Dim numToDo As Integer = CInt(e.Argument)
        For n As Integer = 1 To numToDo
            If bgw.CancellationPending Then
                Exit For
            End If
            System.Threading.Thread.Sleep(200)
            bgw.ReportProgress(n * 10)
        Next
        bgwDone.Set()
    End Sub

    'ReportProgress raises the ProgressChanged event
    Private Sub bgw_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles bgw.ProgressChanged
        ProgressForm.UpdateProgress(e.ProgressPercentage)
    End Sub

    Private Sub bgw_RunWorkerCompleted(sender As Object,
             e As RunWorkerCompletedEventArgs) Handles bgw.RunWorkerCompleted
        ProgressForm.Close()
    End Sub

我的表格与ProgressBar:

Public Class ProgressForm
    Private Sub ButtonCancel_Click(sender As Object, e As EventArgs) Handles ButtonCancel.Click
        Form1.bgwCancelAsync()
    End Sub  

    Public Sub UpdateProgress(pct As Integer)
        ProgressBar1.Value = pct
        ProgressBar1.Refresh()
    End Sub
End Class

1 个答案:

答案 0 :(得分:1)

我不确定你想要完成什么。但是,您的某些代码似乎试图破坏BackGroundWorker的目的:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Cursor = Cursors.WaitCursor
    bgw = New BackgroundWorker
    ...
    If bgw.IsBusy = False Then
        ProgressForm.Show()
        bgw.RunWorkerAsync(10)
    End If

    bgwDone.WaitOne()
    MsgBox("1")
    MsgBox("2")
    Cursor = Cursors.Default
End Sub

BackgroundWorker的目的是在另一个线程上执行一些长时间运行的任务,并使UI保持响应。我不确定只有&#34; 需要几秒钟的任务&#34;有资格作为一个长期运行的任务。

  • 鉴于此,为什么在BGW运行时使用WaitCursor?离开UI resposive的目的是允许用户在此期间做其他事情。
  • bgw.IsBusy的测试永远不会是真的 - 你刚刚创建了3行。再次单击该按钮,您将创建另一个BGW。
  • 点击中的其余代码看起来像,或者希望代码在BGW完成后继续下一行。这不是它的工作原理。
    • 如果应用程序无法在没有完成这些任务的情况下继续,请禁用任何允许用户进入其他地方的内容,直到工作人员完成或:
    • 放弃工作人员并将表单置于等待模式(Me.UseWaitCursor)直到填充内容。这并不排除ProgressBar
  • 如果应用程序将在不同时间使用各种工作人员,则可以使用专用的进度表。 StatusBar可以包含ProgressBar,并且更加微妙(也许是合适的,因为 是一个状态元素)。

因此,修改并使用进度记者的表单实例:

的MainForm

Private WithEvents bgw As BackgroundWorker
Private frmProg As ProgressForm

Public Sub New()
    ' This call is required by the designer.
    InitializeComponent()
    ' Add any initialization after the InitializeComponent() call.
    bgw = New BackgroundWorker
End Sub

Private Sub btnLoadAll_Click(sender As Object, e As EventArgs) Handles btnLoadAll.Click

    bgw.WorkerReportsProgress = True
    bgw.WorkerSupportsCancellation = True

    If bgw.IsBusy = False Then
        ' create ProgressForm instance if needed
        If frmProg Is Nothing Then frmProg = New ProgressForm
        frmProg.Show()
        bgw.RunWorkerAsync(78)
    End If
    btnLoadAll.Enabled = False
End Sub

Private Sub bgw_DoWork(sender As Object, e As DoWorkEventArgs) Handles bgw.DoWork
    ' multiple workers can use the same event
    Dim thisWorker = DirectCast(sender, BackgroundWorker)
    Dim count = Convert.ToInt32(e.Argument)

    For n As Integer = 1 To count
        If thisWorker.CancellationPending Then
            Exit For
        End If
        ' Fake work:
        System.Threading.Thread.Sleep(50)
        ' dont assume the size of the job if
        ' there are multiple BGW or tasks
        thisWorker.ReportProgress(Convert.ToInt32((n / count) * 100))
    Next
End Sub

Private Sub bgw_ProgressChanged(sender As Object,
                        e As ProgressChangedEventArgs) Handles bgw.ProgressChanged
    frmProg.UpdateProgress(e.ProgressPercentage)
End Sub

Private Sub bgw_RunWorkerCompleted(sender As Object,
                        e As RunWorkerCompletedEventArgs) Handles bgw.RunWorkerCompleted
    If e.Error IsNot Nothing Then
        '... ToDo
    ElseIf e.Cancelled Then
        '... ToDo
    Else
        frmProg.Close()
        ' avoid 'cannot access disposed object':
        frmProg = Nothing

        Me.btnNextStep.Enabled = True
        btnLoadAll.Enabled = True
    End If

End Sub

而不是启用&#34; Next&#34;按钮,应用程序可以自动进行。这取决于应用程序。