1

I'm using a background worker to in an Excel VSTO application to throw up a progress dialog box with a status bar and a cancel button to escape from long running calculations. It's working really well, except for one issue. I'd like to use a Modal Dialog, so that the UI behind the dialog gets locked up, instead of a Modeless Dialog. If I use .ShowDialog() instead of .Show(), everything is great until you hit the Cancel button on the form. Following things in the debugger, the cancellation happens, it just takes somewhere in range of 30 seconds. If I use .Show() on my form, then the cancellation occurs immediately as it should.

I'm sure I'm overlooking something fairly simple... Any help would be much appreciated....

Private WithEvents BGW As BackgroundWorker
Private PD As ProgressDialog

Public Sub BGW_DoCalculation(Mode As RunMode)
    'Set the Synchronization Context
    System.Threading.SynchronizationContext.SetSynchronizationContext(New WindowsFormsSynchronizationContext())

    'Setup the Background Worker
    BGW = New BackgroundWorker
    BGW.WorkerReportsProgress = True
    BGW.WorkerSupportsCancellation = True

    'Starts the Background Process
    If BGW.IsBusy = False Then
        BGW.RunWorkerAsync(Mode)
    Else
        Exit Sub
    End If

    'Start the Process Dialog Box
    PD = New ProgressDialog
    'Add a handler to cancel background worker
    AddHandler PD.Cancel_Button.Click, AddressOf CancelBackGroundWorker

    PD.ShowDialog()

End Sub


Private Sub CancelBackGroundWorker(sender As Object, e As System.EventArgs)
    BGW.CancelAsync()
End Sub

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

    If BGW.CancellationPending = False Then
        Dim Mode As RunMode = CType(e.Argument, RunMode)
        LongRunningCalc(Mode)
    Else
        e.Cancel = True
        Return
    End If

End Sub


Private Sub BGW_WorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BGW.RunWorkerCompleted
    PD.Dispose()
    BGW.Dispose()
End Sub


Private Sub BGW_WorkerProgress(ByVal sender As Object, ByVal e As        System.ComponentModel.ProgressChangedEventArgs) Handles BGW.ProgressChanged
    PD.ProgressBar1.Maximum = 100
    If Not BGW.CancellationPending Then
        PD.ProgressBar1.Value = e.ProgressPercentage
        PD.PercentCompleteLabel.Text = e.ProgressPercentage & "%"
        'Update the message Label
        PD.MessageLabel.Text = e.UserState.ToString
    End If
End Sub

'This is shortened for the posting...
Public Sub LongRunningCalc(ByVal Mode As RunMode)
    'Do long running calc - obviously this isn't the calc ;)
    For i = 0 to 1000000000000000
        i+1
    End For

     If BGW.CancellationPending = True Then
            'Report Progress
            BGW.ReportProgress(CInt(((Scen) / TotalScen) * 100), "Canceling Calculation, Please Wait")
            'Output the scenarios that have been calculated...
            Call Globals.ThisWorkbook.OutScen(Scen, Count)
            Return
        End If
End Sub
Eddy
  • 5,320
  • 24
  • 40
GetFuzzy
  • 2,116
  • 3
  • 26
  • 42

2 Answers2

1

I finally after a few hours of reading came back to the post below and implemented an AutoResetEvent

This is the answer that worked for me, and avoided any Application.DoEvent nonsense!

How to wait for a BackgroundWorker to cancel?

Private _resetEvent As New AutoResetEvent(False)

Private Sub CancelBackGroundWorker(ByVal sender As Object, ByVal e As System.EventArgs)
    BGW.CancelAsync()
    _resetEvent.WaitOne()
End Sub

'This is shortened for the posting...
Public Sub LongRunningCalc(ByVal Mode As RunMode)
'Do long running calc - obviously this isn't the calc ;)
For i = 0 to 1000000000000000
    i+1
End For

 If BGW.CancellationPending = True Then
        'Report Progress
        BGW.ReportProgress(CInt(((Scen) / TotalScen) * 100), "Canceling Calculation, Please Wait")
        'Output the scenarios that have been calculated...
        Call Globals.ThisWorkbook.OutScen(Scen, Count)
            _resetEvent.Set()
        Return
    End If
 _resetEvent.Set()
 End Sub
Community
  • 1
  • 1
GetFuzzy
  • 2,116
  • 3
  • 26
  • 42
-1

Pretty sure that adding a regular call to Application.DoEvents will help you (I didn't see any in your code). The cancel click gets added to the windows messages queue. Calling Application.DoEvents explicitly forces the application to process outstanding queue messages.

See also: Application.DoEvents

Eddy
  • 5,320
  • 24
  • 40
  • I used a background worker to avoid using Application.DoEvents... There are several post that point out issues that can occur when using DoEvents. Why would the click event be processed correctly when I use .Show, vs. .ShowDialog? – GetFuzzy Nov 26 '11 at 00:26
  • Have a look at the first answer here: http://stackoverflow.com/questions/360789/in-c-wait-on-the-mainthread-while-continuing-to-process-ui-updates-net-2-0 (modal dialogs are very different from non modal dialogs when it comes to window messaging) – Eddy Nov 26 '11 at 15:15