How to Use Wpf Background Worker

How to use WPF Background Worker


  1. Add using
using System.ComponentModel;

  1. Declare Background Worker:
private readonly BackgroundWorker worker = new BackgroundWorker();

  1. Subscribe to events:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;

  1. Implement two methods:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
}

private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}

  1. Run worker async whenever your need.
worker.RunWorkerAsync();

  1. Track progress (optional, but often useful)

    a) subscribe to ProgressChanged event and use ReportProgress(Int32) in DoWork

    b) set worker.WorkerReportsProgress = true; (credits to @zagy)

Correct usage of BackgroundWorker in WPF

Simple thumb rule is you cannot access UI element from background thread. You are accessing textbox and password control in DoWork which results in an exception.

Fetch user name and password on UI thread and you are good.

string userName = tbLogin.Text;
string password = tbPassword.Password;
bw.DoWork += (o, args) =>
{
user = _viewmodel.Login(userName, password);
};

How to Properly Implement BackgroundWorker in WPF with MVVM / ICommand Pattern

You are using ReportProgress incorrectly and far too often (on every line in the file). It will be being hammered and every call is causing some sort of update in your UI hence locking it up.

ReportProgress is probably easiest to use by passing a percentage to it. I'm not really sure what you are doing in UpdateProgress_Read with the switch. It would be best to only update as you pass a 100th of your total lines.

set your progressBar maximum to 100

ProgressMaximum = 100;

calculate 1% of your total lines

var x = lineCount / 100;
var y = 0;

and only Report progress as you pass each 1%

currentLine++;
if((currentLine % x) == 0)
{
y++;
bw.ReportProgress(y);
}

and change UpdateProgress_Read so it just increments

private void UpdateProgress_Read(object sender, ProgressChangedEventArgs e)
{
vm.IncrementProgress();
}

you'll need to come up with better variable names then x and y! and also work out what to do if you have less than 100 lines in the file.

In C#, is it right approach to use BackgroundWorker when you want to split UI from other logic?

In general, BackgroundWorker is a pretty old and complicated thing. I suggest Task and async/await approach. And maybe IProgress interface + Progress class for callbacks reporting progress.

Let's assume that ParsingStatus is ProgressBar and ParsingPercentage is TextBlock.

IProgress<int> Percentage; // progress callback

// pay attention to 'async' here
private async void GenerateFiles_Click(object sender, RoutedEventArgs e)
{
Percentage = new Progress<int>(ReportProgress); // assign callback, you may move this to constructor

int progress = 0;
int maximum = (int)ParsingStatus.Maximum;
Percentage?.Report(progress); // report progress

FileStream fs = File.OpenRead("file.txt");
long position = 0;
long length = fs.Length;
using (StreamReader sr = new StreamReader(fs))
{
while (!sr.EndOfStream)
{
string line = await sr.ReadLineAsync(); // read file asynchronously
string result = await ParseLine(line); // call parser
// save parsed result here

position += line.Length + Environment.NewLine.Length; // count position
progress = (int)(position * maximum / length); // calculate progress
Percentage?.Report(progress); // report progress

await Task.Delay(50); // just a delay for example.
// never Thread.Sleep() in async methods!
}
}
fs.Close();
}

private async Task<string> ParseLine(string line)
{
string result = "";
// do job here with 'line'
return result;
}

private void ReportProgress(int value)
{
ParsingStatus.Value = value;
ParsingPercentage.Text = value + "%";
}

The key feature of IProgress is passing callback execution to the Thread where it was created.

About splitting UI and other App logiс you may use MVVM programming pattern.

Using BackgroundWorker to complete two methods one after the other WPF/C#


Now the problem I have is, when I use _resetEventOne.WaitOne(); the UI hangs. If I removed those two waits, both methods run asynchronously and the execution moves on and outputs the MessageBox even before those two methods complete.

What am I doing wrong?

When you call WaitOne(), you are blocking the UI thread, causing the UI to hang. If you remove that call, then of course you start both workers at once.

There are several different ways to approach your question. One is to stick as closely to your current implementation, and just fix the barest minimum to get it to work. Doing that, what you'll need to do is perform the actual next statement in the RunWorkerCompleted handler, instead of using an event to wait for the handler to execute.

That looks like this:

public partial class MainWindow : Window
{
public enum MethodType
{
One,
Two
}

private BackgroundWorker worker = null;

private ProgressBarWindow pbWindowOne = null;
private ProgressBarWindow pbWindowTwo = null;

public MainWindow()
{
InitializeComponent();
}

private void btnRun_Click(object sender, RoutedEventArgs e)
{
RunMethodCallers(sender, MethodType.One);
}

private void RunMethodCallers(object sender, MethodType type)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
switch (type)
{
case MethodType.One:
worker.DoWork += MethodOneCaller;
worker.ProgressChanged += worker_ProgressChangedOne;
worker.RunWorkerCompleted += worker_RunWorkerCompletedOne;
break;
case MethodType.Two:
worker.DoWork += MethodTwoCaller;
worker.ProgressChanged += worker_ProgressChangedTwo;
worker.RunWorkerCompleted += worker_RunWorkerCompletedTwo;
break;
}
worker.RunWorkerAsync();
}

private void MethodOneCaller(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(() =>
{
pbWindowOne = new ProgressBarWindow("Running Method One");
pbWindowOne.Owner = this;
pbWindowOne.Show();
});

Utility.TimeConsumingMethodOne(sender);
}

private void MethodTwoCaller(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(() =>
{
pbWindowTwo = new ProgressBarWindow("Running Method Two");
pbWindowTwo.Owner = this;
pbWindowTwo.Show();
});

Utility.TimeConsumingMethodTwo(sender);
}

private void worker_RunWorkerCompletedOne(object sender, RunWorkerCompletedEventArgs e)
{
RunMethodCallers(sender, MethodType.Two);
}

private void worker_RunWorkerCompletedTwo(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("COMPLETED!");
}

private void worker_ProgressChangedOne(object sender, ProgressChangedEventArgs e)
{
pbWindowOne.SetProgressUpdate(e.ProgressPercentage);
}

private void worker_ProgressChangedTwo(object sender, ProgressChangedEventArgs e)
{
pbWindowTwo.SetProgressUpdate(e.ProgressPercentage);
}
}

That said, BackgroundWorker has been made obsolete by the newer task-based API with async and await. With some small changes to your code, it can be adapted to use that newer idiom:

public partial class MainWindow : Window
{
public enum MethodType
{
One,
Two
}

private ProgressBarWindow pbWindowOne = null;
private ProgressBarWindow pbWindowTwo = null;

public MainWindow()
{
InitializeComponent();
}

private async void btnRun_Click(object sender, RoutedEventArgs e)
{
await RunMethodCallers(sender, MethodType.One);
await RunMethodCallers(sender, MethodType.Two);
MessageBox.Show("COMPLETED!");
}

private async Task RunMethodCallers(object sender, MethodType type)
{
IProgress<int> progress;

switch (type)
{
case MethodType.One:
progress = new Progress<int>(i => pbWindowOne.SetProgressUpdate(i));
await Task.Run(() => MethodOneCaller(progress));
break;
case MethodType.Two:
progress = new Progress<int>(i => pbWindowTwo.SetProgressUpdate(i));
await Task.Run(() => MethodTwoCaller(progress));
break;
}
}

private void MethodOneCaller(IProgress<int> progress)
{
Dispatcher.Invoke(() =>
{
pbWindowOne = new ProgressBarWindow("Running Method One");
pbWindowOne.Owner = this;
pbWindowOne.Show();
});

Utility.TimeConsumingMethodOne(progress);
}

private void MethodTwoCaller(IProgress<int> progress)
{
Dispatcher.Invoke(() =>
{
pbWindowTwo = new ProgressBarWindow("Running Method Two");
pbWindowTwo.Owner = this;
pbWindowTwo.Show();
});

Utility.TimeConsumingMethodTwo(progress);
}
}

To do the above does require a small adjustment to the Utility class as well:

static class Utility
{
public static bool TimeConsumingMethodOne(IProgress<int> progress)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(100);
progress.Report(i);
}
return true;
}

public static bool TimeConsumingMethodTwo(IProgress<int> progress)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(50);
progress.Report(i);
}
return true;
}
}

That is, the Progress<T> class takes the place of the BackgroundWorker.ProgressChanged event and ReportProgress() method.

Note that with the above, the code has gotten significantly shorter, simpler, and is written in a more direct way (i.e. related statements are with each other in the same method now).

The example you gave is necessarily simplified. That's perfectly fine, but it does mean that it's not known here what the Thread.Sleep() method represents. In fact, in many cases, this sort of thing can be refactored further such that only the long-running work is done asynchronously. This can sometimes simplify the progress-reporting even further, because it can be done after await-ing each individual asynchronously-executed work component.

For example, let's suppose the work in the loop is either inherently asynchronous or is costly enough that it's reasonable to use Task.Run() to execute each loop iteration. For the purpose of the same, that can be represented using Task.Delay():

static class Utility
{
public static async Task<bool> TimeConsumingMethodOne(Action<int> progress)
{
for (int i = 1; i <= 100; i++)
{
await Task.Delay(100);
progress(i);
}
return true;
}

public static async Task<bool> TimeConsumingMethodTwo(Action<int> progress)
{
for (int i = 1; i <= 100; i++)
{
await Task.Delay(50);
progress(i);
}
return true;
}
}

In the above, I also don't use Progress<T>. Just a simple Action<int> delegate for the caller to use however they want.

And with that change, your window code gets even simpler:

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private async void btnRun_Click(object sender, RoutedEventArgs e)
{
await MethodOneCaller();
await MethodTwoCaller();
MessageBox.Show("COMPLETED!");
}

private async Task MethodOneCaller()
{
ProgressBarWindow pbWindowOne =
new ProgressBarWindow("Running Method One") { Owner = this };
pbWindowOne.Show();

await Utility.TimeConsumingMethodOne(i => pbWindowOne.SetProgressUpdate(i));
}

private async Task MethodTwoCaller()
{
ProgressBarWindow pbWindowTwo =
new ProgressBarWindow("Running Method Two") { Owner = this };

pbWindowTwo.Show();

await Utility.TimeConsumingMethodTwo(i => pbWindowTwo.SetProgressUpdate(i));
}
}

Granted, I took the opportunity to remove the MethodType enum and just call the methods directly, which shortened the code even more. But even if all you did was avoid the use of Dispatcher.Invoke(), that still simplifies the code a lot.

In addition to all that, if you were using data binding to represent the progress state instead of setting the value directly, WPF would handle the cross-thread invocation implicitly for you, so that the Progress<T> class isn't even required even if you can't refactor the Utility class code for it itself to be async.

But, those are minor refinements compared to moving away from BackgroundWorker. I recommend doing that, but whether you invest time in those further refinements is less important.

How to make backgroundworker work in WPF application

You've got to set the ApartmentState before the thread is started.

http://blogs.msdn.com/b/jfoscoding/archive/2005/04/07/406341.aspx

To set the apartment state on your WPF App,

add the [STAThread] attribute to your application, like this:

public partial class App : Application
{
App()
{
InitializeComponent();
}

[STAThread]
static void Main()
{
Window1 window = new Window1();
App app = new App();
app.Run(window);
}
}

Is there a way to slow down a BackgroundWorker in WPF?

rather than the last line in frmMain_Loaded:

m_bgWorker.ReportProgress(100); 

I would put a for loop with Sleep(), for simulating some extra processing:

for (int i=0; i<=100; i+=10)
{
if (SystemBO.IsOnline())
i = 100;
Sleep(1000);
m_bgWorker.ReportProgress(i);
}

Using background Worker to insert Text file into a listBox WPF

Most important rule: you can only touch the GUI from the GUI (main) thread.

Your Mylist.Add(line); is breaking that rule.

As an alternative you can use the Progress event:

  backgroundWorker1.ReportProgress(percentage, line);

and then

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)// progress change
{
progressBar1.Value = e.ProgressPercentage;
string line = (string) e.State;
Mylist.Add(line); // ok, this runs on the main thread
}

Background worker within a function in c# wpf

If you call the ReportProgress method too rapidly, it's possible that the UI thread will not have a chance to process the "progress" and update appropriately before the BackgroundWorker is hitting it again.

private void doWork(object sender, DoWorkEventArgs e)
{
var wrk = sender as BackgroundWorker;

// obviously you wouldn't really do this :)
while(true)
wrk.ReportProgress(0);
}

To see the effect you're looking expecting, you could set an artificial "pause" in your DoWork event, in order to give the UI time to update appropriately:

private void doWork(object sender, DoWorkEventArgs e)
{
var wrk = sender as BackgroundWorker;

var p = 0;
while(true)
{
wrk.ReportProgress(p++);
Thread.Sleep(100);
}
}

As for your situation, if the code is executing that quickly, you may not actually need to be executing it in a separate thread.

Alternatively, you could update your UI to say "Please wait. Loading...", then do everything you need to do in the BackgroundWorker, and just return the final result back to the UI at the end.



Related Topics



Leave a reply



Submit