Using the Background Worker Process for Long Running Processes in WPF and Silverlight

Using the Background Worker Process for Long Running Processes in WPF and Silverlight

Using the Background Worker Process for Long Running Processes in WPF and Silverlight

In a previous article WPF DataBinding using Visual Basic (VB.NET), a problem was encountered when trying to keep the User Interface active during Long Running Processes as a result of a button click.  Either the whole system would hang until the process was completed or the program would crash.

This hang-up is due to the fact that both the Long Running Process and the UI are using the same thread.  The solution is to run the Long Running Process in a separate thread.  The BackgroundWorker component, gives you the ability to execute Long Running Processes in the background asynchronously, on a separate thread from the application’s main UI thread.

I have just started a project which is going to have quite a few of these Long Running Processes and so what I need is a simple implementation of the Background Worker  process which I can expand as required.

The implementation consists of a WPF page with a start button, a cancel button and three labels to let the user know what is happening before, during and after the Long Running Process has been started by the start button.


<Page x:Class="BackGroundWorkerExample"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="BackGroundWorkerExample"
    x:Name="BackGroundWorkerExample"  WindowTitle="WPF Luddite Test Bed: Background Worker Example" Width="1024" Height="768">
    <StackPanel>
        <Button x:Name="btnStartBackgroundProcess" Width="300" Height="30" Margin="0 20 0 0"
            Content="Start Background Process">
        </Button>
        <Label x:Name="lblStartedCompleted" Content="" Width="500" Height="30" Margin="0 20 0 0">
        </Label>
        <Label x:Name="lblCounter" Content="" Width="500" Height="30" Margin="0 20 0 0">
        </Label>
        <Label x:Name="lblPercentCompleted" Content="" Width="500" Height="30" Margin="0 20 0 0">
        </Label>
        <Button x:Name="btnCancelBackgroundProcess" Width="300" Height="30" Margin="0 20 0 0"
            Content="Cancel Background Process" IsEnabled="False">
        </Button>

    </StackPanel>
</Page>

The following steps will be required in the code behind:

1.  Define a new BackgroundWorker, create an instance and do some setup.

2. Define the LongRunningProcess – must not reference the User Interface (UI).

3. Define a subroutine to handle the BackgroundWorker DoWork event.

4. Define a subroutine to handle the BackgroundWorker ProgressChanged event.

5. Define a subroutine to handle the BackgroundWorker RunWorkerCompleted event.

6. Define a subroutine to handle the event caused by the Start button being clicked.

7. Define a subroutine to handle the event caused by the Cancel button being clicked.


Imports System.ComponentModel

Partial Public Class BackGroundWorkerExample

    '
    '   Define a new backgroung worker.
    '
    Private WithEvents bw As New BackgroundWorker()
    '
    ' Create new instance of BackGroundWorker and do some setup
    '
    Public Sub New()

        InitializeComponent()

        bw.WorkerSupportsCancellation = True
        bw.WorkerReportsProgress = True

    End Sub

    Private Function LongRunningProcess() As String
        '
        '   The LongRunngProcess should NOT refer to any UI objects.
        '
        Dim iteration As Integer = CInt(100000000 / 100)
        Dim cnt As Long = 0

        For i As Long = 0 To 100000000
            '
            '   Bring the LongRunningProcess to an orderly termination if the Cancel button is clicked.
            '   - see further comments in routine that handles the btnCancelBackgroundProcess_Click event.
            '
            If bw.CancellationPending Then
                Return ""
            End If

            cnt = cnt + 1

            '
            '   Report Progress:
            '   When you need the background operation to report on its progress,
            '   you can call the ReportProgress method to raise the ProgressChanged event.
            '   The ReportProgress Method permits up to 2 parameters (ProgressChangedEventArgs):
            '       Paramater 1. Is of type Integer and is defined as "percentProgress"
            '       Paremeter 2. Is of type Object and is defined as "userState"
            '       I see no reason why you can't return any information you want as long as you conform to the
            '       parameter types integer and object.
            '
            '   In a simple example like this, if the ReportProgress method was called for every iteration of i
            '   then the system would probably hang the user interface and run out of memory. Therefore, in this instance, we
            '   only report back on every 10,000 iterations.
            '
            If (i Mod 10000 = 0) _
                And (bw IsNot Nothing) _
                AndAlso bw.WorkerReportsProgress Then
                '
                '   Call ReportProgress
                '
                bw.ReportProgress(i \ iteration, cnt)
            End If

        Next

        Return cnt.ToString()

    End Function

    Private Sub backgroundWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles bw.DoWork
        '
        ' call long running process and get result
        '
        e.Result = Me.LongRunningProcess()
        '
        ' Cancel if cancel button was clicked.
        '
        If bw.CancellationPending Then
            e.Cancel = True ' This sets the Cancelled property of the RunWorkerCompletedEventArgs
            Return
        End If

    End Sub

    Private Sub backgroundWorker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bw.ProgressChanged
        '
        '   Use the ProgressChanged Sub to give user feedback via the UI.
        '
        ' Update UI with values contained in ProgressChangedEventArgs e.ProgressPercentage and e.UserState.
        '
        Me.lblPercentCompleted.Content = CStr(e.ProgressPercentage) & "% Completed."
        lblCounter.Content = CStr(e.UserState)
        ''
    End Sub

    Private Sub backgroundWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles bw.RunWorkerCompleted
        '
        ' Back on primary thread, can access ui controls
        '
        If e.Cancelled Then
            Me.lblStartedCompleted.Content = "Process Cancelled."
        Else
            Me.lblStartedCompleted.Content = "Background Process Completed. "
        End If
        '
        Me.btnStartBackgroundProcess.IsEnabled = True
        Me.btnCancelBackgroundProcess.IsEnabled = False
        ''
    End Sub

    Private Sub btnStartBackgroundProcess_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnStartBackgroundProcess.Click
        '
        lblStartedCompleted.Content = "Background Process Started..."
        Me.btnStartBackgroundProcess.IsEnabled = False
        Me.btnCancelBackgroundProcess.IsEnabled = True
        '
        ' Calls DoWork on secondary thread
        '
        bw.RunWorkerAsync()
        '
        ' RunWorkerAsync returns immediately.
        ''
    End Sub

    Private Sub btnCancelBackgroundProcess_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnCancelBackgroundProcess.Click
        '
        '   There are several points to note when cancelling a process:
        '
        '   1. The WorkerSupportsCancellation property must have been initialised to true.
        '
        '   2. The CancelAsync method does not immediately cancel the process, but instead sets the
        '      CancellationPending property to True.  It is the therefore important that the LongRunningProcess
        '      periodically checks the CancellationPending property and if True the process should be
        '      terminated in an orderly manner.
        '
        '   3. The CancellationPending property of the background worker process should also be checked by the
        '      routine handling the DoWork method after the LongRunningProcess has completed.
        '      If the CancellationPending Property is true then the DoWork routine should set Cancel
        '      property of the DoWorkEventArgs to True.
        '      CancelAsync() method.  This should be checked by the routine which is handling the RunWorkerCompleted
        '      event.
        '
        '   4. It is good practice to ensure theat the Cancel button is only enabled when the LongRunningProcess
        '      is actually running.  Therefore the button should be disabled by default, only enabled when the
        '      process is started, and disable again when the process is completed or cancelled.
        '
        bw.CancelAsync()
        '
        btnCancelBackgroundProcess.IsEnabled = False
        '
    End Sub

End Class

Hope this helps.

Advertisements

Where are the VB Books for WPF, WCF, WF, Linq & Silverlight?

I definitely prefer VB to C#.  I like its readability.  After all, a programming language should be readable by humans, that is its main purpose. C# with all of its different brackets [{<:%;>})] and punctuation is a 2-hour bug hunt waiting to happen and that is before you need to worry about the semantics of your code. No, I have left C# well alone since .NET was born.

However, with all of the new developments from Microsoft recently VB has had to take a back seat.  Not that VB is lacking in functionality, but it certainly is becoming a second class citizen as far as the technical book writers are concerned.  All of the new books concentraing on .NET 3.5 functionality, such as WPF, WCF, WF, Silverlight and Linq, are being written for C# users. I am not aware of any books written this year which has chosen to give samples using VB as the primary language.

There appears little point in continuing with VB, if I am going to need C# expertise to learn all of these new technologies.

So I have a few of questions:

1. Are Microsoft aware of this and do they propose to remedy the situation with the publication of more VB orientated books?

2. Are you aware of any books written this year covering .NET 3.5 topics such as WPF, WCF, WF, Silverlight and Linq, which use VB as the primary language?

3. Should I cave and give up VB and my code library and start coding in C#?

Microsoft Visual Basic 2008 Step by Step

Microsoft Visual Basic 2008 Step by Step

 

Microsoft Visual C# 2008 Step by Step

Microsoft Visual C# 2008 Step by Step