I'm using WPF and I have main thread which is GUI (wizard).
When user click Finish on wizard it open second thread which display user progress bar used in background worker.
In Main thread I doing:
MessageWithProgressBar progress = new MessageWithProgressBar();
progress.Show();
createFilesInA();
createFilesInB();
createFilesInC();
createFilesInD();
createFilesInE();
createFilesInF();
createFilesInG();
createFilesInH();
createFilesInI();
createFilesInJ();
createFilesInK();
In each createFiles method I increment by 1 the static variable called currentStep which I used it in background worker as detailed below.
In background worker I doing:
public partial class MessageWithProgressBar : Window
{
private BackgroundWorker backgroundWorker = new BackgroundWorker();
public MessageWithProgressBar()
{
InitializeComponent();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.ProgressChanged += ProgressChanged;
backgroundWorker.DoWork += DoWork;
backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
}
private void DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(100);
int i = GeneralProperties.General.currentStep;
if (i > GeneralProperties.General.thresholdStep)
{
progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new DispatcherOperationCallback(delegate
{
progress.Value = 100;
title.Content = progress.Value.ToString();
return null;
}), null);
return;
}
else
{
progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new DispatcherOperationCallback(delegate
{
progress.Value = (int)Math.Floor((decimal)(8 * i));
progressLabel.Text = progress.Value.ToString();
return null;
}), null);
}
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new DispatcherOperationCallback(delegate
{
progress.Value = e.ProgressPercentage;
return null;
}), null);
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progress.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new DispatcherOperationCallback(delegate
{
progress.Value = 100;
title.Content = progress.Value.ToString();
return null;
}), null);
WindowMsgGenDB msg = new WindowMsgGenDB();
msg.Show();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (backgroundWorker.IsBusy == false)
{
backgroundWorker.RunWorkerAsync();
}
}
}
The main thread updated variable called currentStep and the second thread used it to report on the main thread progress.
The operations of the main thread takes a few seconds (not more 15 seconds)
I have two issues:
I see on progress bar only when currentStep=2 (then the progress is 16) and then the progress is 100, and I don't see every step
At the beginning, the progress bar is freeze and it seems like it stuck.
(maybe it connects to the call progress.Show() from the main thread?)
Thanks!
As far as I understand your code your background worker is not doing anything, really. It updates the progress once and that's it.
Also: using global static variables to communicate between a form and a background worker - ouch...
Also, you're using it wrong in my opinion. The work (CreateFilesInA ... CreateFilesInK) should be done by the background worker - that's what it is for. As the main thread will be blocked the way you implemented it, you will not see any updates otherwise.
The usual way to implement something like this is:
Create progress window and disable UI
Start background worker that does stuff in DoWork. In DoWork, after every call to a CreateFilesInXYZ method, call ReportProgress to the the UI be updated.
Update stuff in progress window whenever ProgressChanged event is fired
Hide progress window and enable your application's UI when background worker is done
The way you're doing it it's in no way asynchronous. So, actually, your code should look something like this:
public partial class MainWindow : Window
{
private BackgroundWorker backgroundWorker = new BackgroundWorker();
private MessageWithProgressBar progressWindow;
public MainWindow()
{
InitializeComponent();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.ProgressChanged += ProgressChanged;
backgroundWorker.DoWork += DoWork;
backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
progressWindow = new MessageWithProgressBar();
progressWindow.Owner = this;
progressWindow.Show();
backgroundWorker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
int numSteps = 11;
int currentStep = 0;
int progress = 0;
CreateFilesInA();
currentStep += 1;
progress = (int)((float)currentStep / (float)numSteps * 100.0);
worker.ReportProgress(progress);
CreateFilesInB();
currentStep += 1;
progress = (int)((float)currentStep / (float)numSteps * 100.0);
worker.ReportProgress(progress);
// All other steps here
...
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressWindow.progress.Value = e.ProgressPercentage;
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressWindow.Close();
WindowMsgGenDB msg = new WindowMsgGenDB();
msg.Show();
}
}
Please note that the above code goes into your main window! The MessageWithProgressWindow does not contain any code. Maybe the Window_Loaded event handler is not the right place to start the background worker, but you get the picture.
Related
public static void CalculateAttributions(BackgroundWorker worker, string _filename, ComboBox cmb, OpenFileDialog open)
{
worker = new BackgroundWorker { WorkerReportsProgress = true };
while (wave.Position != length)
{
...Process..
worker.ReportProgress((100 * (int)(length / wave.Position)) / (int)(length / mainBuffer.Length));
}
}
I wrote this method in a class to perform my calculations and using BackgroundWorker as parameter to show a Progressbarduring loop. However when i run this method in Form.cs
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = Math.Min(e.ProgressPercentage, 100);
}
this event is not working so ProgressBar value not changed. How can i make it work?
The report Progress event you have given here is for the backgroundworker1 and not the worker you coded within the function CalculateAttributions(...)
To enbale report progress and do progress bar changes.. Try the following code
Worker1.ProgressChanged+=new delegate {
progressBar1.Value = Math.Min(e.ProgressPercentage, 100);
};
with in the same function.
Hope this helps you
You will need to attach your event handlers. It should go like this.
public static void CalculateAttributions(BackgroundWorker worker, string _filename, ComboBox cmb, OpenFileDialog open)
{
worker = new BackgroundWorker { WorkerReportsProgress = true };
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
if (worker.IsBusy != true)
{
worker.RunWorkerAsync();
}
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (wave.Position != length)
{
...Process..
worker.ReportProgress((100 * (int)(length / wave.Position)) / (int)(length / mainBuffer.Length));
}
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = Math.Min(e.ProgressPercentage, 100);
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//done
}
I think you should check this link.
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
The idea is to get your work inside background worker. and after each milestone report progress on your progress bar.
create background worker object
attach event handlers
call RunWorkerAsynch()
add code inside event handlers
do work will have the real work and report to progress call
report to progress will simply tell your progress bar to move one step ahead
completed will be called once everything is done. you may want to show a message to user that the operation is completed.
I want to modify interval value of timer which is instance of System.Microsoft.Timer from a worker thread
When i change this value in the thread running worker thread, Timer is stopped.
let see my source code
private void Scan_Screen(object sender, EventArgs e)
{
textBox1.Text += "a";
}
private void button1_Click(object sender, EventArgs e)
{
g_RECEIVER_timer = new System.Windows.Forms.Timer();
g_RECEIVER_timer.Enabled = true;
g_RECEIVER_timer.Interval = TIMER_INTERVAL;
g_RECEIVER_timer.Tick += new EventHandler(Scan_Screen);
}
private void button2_Click(object sender, EventArgs e)
{
g_Control_Thread = new Thread(new ParameterizedThreadStart(Control_Message_Receiver));
g_Control_Thread.Start(200);
}
//thread function
public void Control_Message_Receiver(object v)
{
g_RECEIVER_timer.Stop();
g_RECEIVER_timer.Interval = 200;
g_RECEIVER_timer.Enabled = true;
g_RECEIVER_timer.Tick += new EventHandler(Scan_Screen);
}
Why this happening is occurred? Also how can i make this run? (I want to adjust interval value of timer in the worker thread)
You need to invoke this Control_Message_Receiver on the UI thread since you have spawned another worker thread and you are accessing objects on the UI thread context.
And don't need to re-declare the Tick event on your worker thread method.
Look at this snippet below:
private void Scan_Screen(object sender, EventArgs e)
{
textBox1.Text += "a";
}
private void button1_Click(object sender, EventArgs e)
{
g_RECEIVER_timer = new System.Windows.Forms.Timer();
g_RECEIVER_timer.Enabled = true;
g_RECEIVER_timer.Interval = 1000;
g_RECEIVER_timer.Tick += new EventHandler(Scan_Screen);
}
private void button2_Click(object sender, EventArgs e)
{
Thread g_Control_Thread = new Thread(new ParameterizedThreadStart(Control_Message_Receiver));
g_Control_Thread.Start(1);
}
//thread function
public void Control_Message_Receiver(object v)
{
//timer1.Stop(); //why stop? -- remove this instead
IntervalChange((int)v); //call this method and invoke it on the UI thread
g_RECEIVER_timer.Enabled = true;
//timer1.Tick += new EventHandler(Scan_Screen); // -- remove this
}
delegate void intervalChanger(int time);
void ChangeInterval(int time)
{
g_RECEIVER_timer.Interval = time;
}
void IntervalChange(int time)
{
this.Invoke(new intervalChanger(ChangeInterval), new object[] {time}); //invoke on the UI thread
}
It may be better to use System.Threading.Timer. This one can be readjusted from any thread. Note, however, that the timer callback runs in a threadpool thread. So you have to use Invoke, if you need to access the GUI from this callback.
In my application I need to perform a series of initialization steps, these take 7-8 seconds to complete during which my UI becomes unresponsive. To resolve this I perform the initialization in a separate thread:
public void Initialization()
{
Thread initThread = new Thread(new ThreadStart(InitializationThread));
initThread.Start();
}
public void InitializationThread()
{
outputMessage("Initializing...");
//DO INITIALIZATION
outputMessage("Initialization Complete");
}
I have read a few articles about the BackgroundWorker and how it should allow me to keep my application responsive without ever having to write a thread to perform lengthy tasks but I haven't had any success trying to implement it, could anyone tell how I would do this using the BackgroundWorker?
Add using
using System.ComponentModel;
Declare Background Worker:
private readonly BackgroundWorker worker = new BackgroundWorker();
Subscribe to events:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
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
}
Run worker async whenever your need.
worker.RunWorkerAsync();
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)
You may want to also look into using Task instead of background workers.
The easiest way to do this is in your example is Task.Run(InitializationThread);.
There are several benefits to using tasks instead of background workers. For example, the new async/await features in .net 4.5 use Task for threading. Here is some documentation about Task
https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task
using System;
using System.ComponentModel;
using System.Threading;
namespace BackGroundWorkerExample
{
class Program
{
private static BackgroundWorker backgroundWorker;
static void Main(string[] args)
{
backgroundWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
backgroundWorker.DoWork += backgroundWorker_DoWork;
//For the display of operation progress to UI.
backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
//After the completation of operation.
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync("Press Enter in the next 5 seconds to Cancel operation:");
Console.ReadLine();
if (backgroundWorker.IsBusy)
{
backgroundWorker.CancelAsync();
Console.ReadLine();
}
}
static void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 200; i++)
{
if (backgroundWorker.CancellationPending)
{
e.Cancel = true;
return;
}
backgroundWorker.ReportProgress(i);
Thread.Sleep(1000);
e.Result = 1000;
}
}
static void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Console.WriteLine("Completed" + e.ProgressPercentage + "%");
}
static void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
Console.WriteLine("Operation Cancelled");
}
else if (e.Error != null)
{
Console.WriteLine("Error in Process :" + e.Error);
}
else
{
Console.WriteLine("Operation Completed :" + e.Result);
}
}
}
}
Also, referr the below link you will understand the concepts of Background:
http://www.c-sharpcorner.com/UploadFile/1c8574/threads-in-wpf/
I found this (WPF Multithreading: Using the BackgroundWorker and Reporting the Progress to the UI. link) to contain the rest of the details which are missing from #Andrew's answer.
The one thing I found very useful was that the worker thread couldn't access the MainWindow's controls (in it's own method), however when using a delegate inside the main windows event handler it was possible.
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
pd.Close();
// Get a result from the asynchronous worker
T t = (t)args.Result
this.ExampleControl.Text = t.BlaBla;
};
I have a simple app which fires of a series of data intensive tasks. I'm not very experienced with WinForms and I was wondering the best way to do this without locking the interface. Can backgroundWorker be re-used, or is there another way to do this?
Thanks
BackgroundWorker is a thread that also includes notification synchronization. For example, if you wanted to update your UI when the scan completes, a regular Thread cannot access the UI objects (only the UI thread can do that); so, BackgroundWorker provides a Completed event handler that runs on the UI thread when the operation completes.
for more info see: Walkthrough: Multithreading with the BackgroundWorker Component (MSDN)
and a simple sample code:
var worker = new System.ComponentModel.BackgroundWorker();
worker.DoWork += (sender,e) => Thread.Sleep(60000);
worker.RunWorkerCompleted += (sender,e) => MessageBox.Show("Hello there!");
worker.RunWorkerAsync();
backgroundWorker can be used.
its benefit - it allows you to update a progress bar and interact with UI controls. (WorkerReportsProgress)
Also it has a cancellation mechanism. (WorkerSupportsCancellation)
You can use BackgroundWorker for such requirements. Below is a sample which updates a label status based on percentage task [long running] completion. Also, there is a sample business class which sets some value and the value is set back to UI via ProgressChanged handler. DoWork is the place where you write your long running task logic. Copy-Paste the code below after adding a label and backgroundworker component on a Winforms app & give it a shot. You may debug across various handler [RunWorkerCompleted, ProgressChanged, DoWork] and have a look at InitWorker method. Notice the cancellation feature too.
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form3 : Form
{
private BackgroundWorker _worker;
BusinessClass _biz = new BusinessClass();
public Form3()
{
InitializeComponent();
InitWorker();
}
private void InitWorker()
{
if (_worker != null)
{
_worker.Dispose();
}
_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.ProgressChanged += ProgressChanged;
_worker.RunWorkerAsync();
}
void DoWork(object sender, DoWorkEventArgs e)
{
int highestPercentageReached = 0;
if (_worker.CancellationPending)
{
e.Cancel = true;
}
else
{
double i = 0.0d;
int junk = 0;
for (i = 0; i <= 199990000; i++)
{
int result = _biz.MyFunction(junk);
junk++;
// Report progress as a percentage of the total task.
var percentComplete = (int)(i / 199990000 * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
// note I can pass the business class result also and display the same in the LABEL
_worker.ReportProgress(percentComplete, result);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// Display some message to the user that task has been
// cancelled
}
else if (e.Error != null)
{
// Do something with the error
}
}
void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
}
}
public class BusinessClass
{
public int MyFunction(int input)
{
return input+10;
}
}
}
The background worker would be a good choice to start with
For more info look here
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Hi
I am trying to integrate a background worker with a progress bar but cannot get it right.
I am processing some files and all the processing is done in an external class.
My difficulty is that the looping is inside this class,where usually I do the talking to the backgroundworker.
The good thing is that when processing a file,an event is fired whenever each file has completed processing.
This is my code any suggestions how could this be achieved
BackgroundWorker _bw = new BackgroundWorker
private void RunLongProcess()
{
_bw.WorkerReportsProgress = true;
_bw.WorkerSupportsCancellation = true;
_bw.DoWork += DoWork;
_bw.ProgressChanged += ProgressChanged;
_bw.RunWorkerCompleted += RunWorkerCompleted;
_bw.RunWorkerAsync();//start the process
if (_bw.IsBusy)
_bw.CancelAsync();
}
static void DoWork (object sender, DoWorkEventArgs e)
{
var files=GetFiles();
int fileCount=files.Count;
//usually I do a loop here but all the processing is done inside this class so
var fileProcessor=new FileProcesser();
fileProcessor.ProcessFiles(files);
}
private void OnFileProcessCompleted(object sender, FileEventArgs e)
{
//Event Fired when a file has been processed
//How do I update progressBar.Problem cross threading here.
//What do I do here?????
_bw.ReportProgress(e.FileProcessedCount, e);
}
ProgressChanged (object sender, ProgressChangedEventArgs e)
{
// Update the UI
labelProgress.Text = e.UserState;
progressBar.Value = e.ProgressPercentage;
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
// Console.WriteLine("You canceled!");
else if (e.Error != null)
//Console.WriteLine("Worker exception: " + e.Error.ToString());
else
// Console.WriteLine("Complete: " + e.Result);
}
_bw.ReportProgress(e.FileProcessedCount, e); should not go in OnFileProcessCompleted. This event is fired when DoWork completes. You should place that in DoWork to update the progress bar. So it would look something like this:
private void DoWork (object sender, DoWorkEventArgs e)
{
BackgroundWorker bg = sender as BackgroundWorker;
var files = GetFiles();
int fileCount = files.Count;
var fileProcessor = new FileProcesser();
for(int i = 0; i < fileCount; i++)
{
fileProcessor.ProcessFile(files[i]);
bg.ReportProgress( (uint)((i / (double)fileCount) * 100));
}
}
I would pass in the BackgroundWorker to the FileProcessor like this:
private void DoWork (object sender, DoWorkEventArgs e)
{
BackgroundWorker bg = sender as BackgroundWorker;
var files = GetFiles();
int fileCount = files.Count;
var fileProcessor=new FileProcesser(bg);
fileProcessor.ProcessFiles(files);
}
In the FileProcesser, it would look something like this:
private BackgroundWorker _bg;
public FileProcessor(BackgroundWorker bg)
{
_bg = bg;
}
public void ProcessFiles(Files files)
{
// Process files
// ...
// Report Progress
_bg.ReportProgress(e.FileProcessedCount, e);
}
In that case, one solution is to make a derived class from BackgroundWorker, make it subscribe to the event, and have it send the progress event to the UI thread in the event handler.