Monday, 10 February 2014

Gem #3: Background Workers

Last one for tonight. It goes sort of hand-in-hand with the XML resources. I wanted to show in the application some background algorithmic processing as I was working through some of the data in the Resource files (see Gem#2). The thing for this is a background worker thread:

Code examples everywhere, but again, as a placeholder for me:


BackgroundWorker bw = new BackgroundWorker();

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    InitializeComponent();

    bw.WorkerReportsProgress = true;
    bw.WorkerSupportsCancellation = true;
    bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
    bw.DoWork += new DoWorkEventHandler(bw_DoWork);
    bw.RunWorkerCompleted += new 
           RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

    bw.RunWorkerAsync();
}

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    
    // do the time consuming operation 
    
}

using System.ComponentModel; needs to be included;

Now, the usual way to report progress in the time-consuming process is to use the following little snippet:


// in the time-consuming process, 
// where percent is a double with percentage complete
worker.ReportProgress(percent);


private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}

Which is all well and good if you're just showing a progress bar or similar, but in my case I wanted to show an update of the components that had been processed, which was a text message. As the long-running thread is not on the GUI thread none of the GUI components can be directly touched as they go cross-thread so some alternatives are needed. The usual Silverlight route out of this is to use the dispatcher which works quite nicely:


// in the time-consuming process
this.Dispatcher.BeginInvoke(delegate()
{
    // do the GUI stuff in here
}
// back the time-consuming worker thread

And for the sake of completeness the usual way to enable cancellation of this thread is as follows:


// in the time-consuming process
// periodically check if the thread has been
// cancelled and break out, seeting e.Cancel
if ((worker.CancellationPending == true))
{
    e.Cancel = true;
}

this would be signalled in the GUI to set bw.CancellationPending = true. and the cancellation status would be passed through to the completion handler.

// and this can be used in the completed 
// handler
if ((e.Cancelled == true))
{
    this.tbProgress.Text = "Canceled!";
}
    e.Cancel = true;
}

Details of this and links to the rest of the BackgroundWorker methods are here.


No comments:

Post a Comment