Reputation: 4455
I have already implemented a functionnal application that parses 26 pages of html all at once to produce an xml file with data contained on the web pages. I would need to implement a thread so that this method can work in the background without causing my app to seems unresponsive.
Secondly, I have another function that is decoupled from the first one which compares two xml files to produce a third one and then transform this third xml file to produce an html page using XSLT. This would have to be on a thread, where I can click Cancel to stop the thread whithout crashing the app.
What is the easiest best way to do this using WPF forms in VS 2010 ?
I have chosen to use the BackgroundWorker.
BackgroundWorker implementation:
public partial class MainWindow : Window
{
private BackgroundWorker bw = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.LoadFiles();
}
private void btnCompare_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
{
progressBar2.IsIndeterminate = true;
// Start the asynchronous operation.
bw.RunWorkerAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
StatsProcessor proc = new StatsProcessor();
if (lstStatsBox1.SelectedItem != null)
if (lstStatsBox2.SelectedItem != null)
proc.CompareStats(lstStatsBox1.SelectedItem.ToString(), lstStatsBox2.SelectedItem.ToString());
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar2.IsIndeterminate = false;
progressBar2.Value = 100;
}
I have started with the bgworker solution, but it seems that the bw_DoWork method is never called when btnCompare is clicked, I must be doing something wrong... I am new to threads.
Upvotes: 2
Views: 1035
Reputation: 161002
If you're new to threading I think the easiest to start with would be using a BackgroundWorker:
The BackgroundWorker is event-driven so it's much easier to get your ahead around if you're new to multi-threading. The .NET 4 Task library is much more flexible, but a little more involved to take advantage of especially with UI updates.
Example:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler((o, args) =>
{
//Long running stuff here
Thread.Sleep(10000);
string result = "Hi UI!";
args.Result = result;
});
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((o, args) =>
{
if (args.Result != null)
{
SomeTextBox.Text = args.Result;
}
});
worker.RunWorkerAsync();
Upvotes: 2
Reputation: 2920
Since you're using .NET 4 and VS2010, I would use the Task Parallel Library. Here's an example that shows the basics of how to background a task and marshal UI updates back to the GUI. I think you need to use a BlockingCollection to implement the second part.
There is a good blog post that talks about chaining tasks and supporting cancellation: http://blogs.msdn.com/b/csharpfaq/archive/2010/08/12/blocking-collection-and-the-producer-consumer-problem.aspx
Here's the example of back-grounding without a background worker
MainWindow.xaml - it's just a window with a status label. The min necessary to get the point across:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="378" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="94" SizeToContent="Height">
<Grid>
<StackPanel>
<Button Click="Button_Click">Press Me</Button>
<StatusBar>
<Label Name="TheLabel" Content="Status" />
</StatusBar>
</StackPanel>
</Grid>
</Window>
Here's the Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading.Tasks;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
// Dispatchers are an easy way to update the UI from a background thread
private System.Windows.Threading.Dispatcher Dispatcher;
// the Current Task - think of this as your background worker
private Task CurrentTask;
public MainWindow()
{
InitializeComponent();
// this line needs to run on the main thread.
Dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
}
private void UpdateStatus(string msg)
{
// marshall the call back to the main thread
Dispatcher.Invoke( new Action( () => TheLabel.Content = msg ));
}
// we're going to calculate the average of a sh*tload (technical term) of numbers
private void DoSomeWork()
{
// update the UI
UpdateStatus("Doing Work");
Random r = new Random();
int max = r.Next(100000000, 1000000000);
double avg = Enumerable.Range(0, max).Average();
// update the UI
UpdateStatus(string.Format("Done - Average = {0}", avg));
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// make sure the current task isn't already running
if (CurrentTask != null && CurrentTask.IsCompleted == false)
{
MessageBox.Show("Wait");
return;
}
// start a new one
CurrentTask = Task.Factory.StartNew(() =>
{
DoSomeWork();
});
}
}
}
Upvotes: 1
Reputation: 694
You can easily do multithreading using Threadpools. However, the trouble and issues you will have everything to do with your code that produces this XML file. If you restrict this code to only be running one thread at a time - but your UI still responsive, you will have less issues with your code. (Instead of trying to run a thread and process multiple web pages at one time)
Upvotes: 0