How to check the process progress using background worker - c#

I have a method that use in downloading files from server . MY method works fine, however i want to return a calculated percentage of the process execute at each instance of the execution. i tried to use backgroundworker and use the backgroundworker_changed method for my progressbar as below.
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
Start worker on button click.
private void btnStart_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
worker_changed method
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProcessPercentage;
}
My problem is , i understand i have to report progress from the DoWork() method. Please how do i achieve that? I thought of doing something like this.
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = Convert.ToInt32( e.UserState );
}
It still does not fire as there was no progress returned from the DoWork. Any help would be appreciated.

You need to call ReportProgress from your DoWork method along with the precentage int value.
For example:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//you can safely pass data out of the thread via the 'obj'
bw.ReportProgress(0, obj);
}

As explained in this official tutorial from msdn. You have to report progress changes by yourself inside your DoWork method. Here's is an example :
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
worker.ReportProgress(10/*the percentage you want*/);
}
The BackgroundWorker.ProgressChanged event :
is raised when you call the ReportProgress method
and it is designed to :
add code to indicate the progress, such as updating the user
interface.
The BackgroundWorker.RunWorkerCompleted event :
occurs when the background operation has completed, has been canceled,
or has raised an exception
Both events are intended to be called when your DoWork method failed, ended or called the ReportProgress method. So in another words, we can say that you're the one deciding when they occur. It cannot be done without an action of yours, it's your process and only you know when it is 20%, 30%or 100% (and so on...) completed.

Related

BackgroundWorker Show Progress While Method Is Being Executed

I have a BackgroundWorker and Progressbar. On The DoWork event of the backgroundworker I'm executing a method that may take 2-5 seconds to complete but may take even longer sometimes. I want to show progress in the progressbar to indicate that something is still executing in the background. Most of the examples I found in the web are tasks from a loop but as I said my DoWork is executing a long running process and there's no way for me to determine the percentage of the progress. Please help. Here's my code.
private void MyForm_Load(object sender, EventArgs e)
{
Shown += new EventHandler(MyForm_Shown);
// To report progress from the background worker we need to set this property
backgroundWorker1.WorkerReportsProgress = true;
// This event will be raised on the worker thread when the worker starts
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
// This event will be raised when we call ReportProgress
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += BackgroundWorker1_RunWorkerCompleted;
}
private void MyForm_Shown(object sender, EventArgs e)
{
// Start the background worker
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
GetData();//this method may take 2-5 seconds to complete. I want to show progess in the progressbar while method execution is not yet finished.
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Perform something on complete
}

Asynchronously add controls to Form during Form_Load

I'm working on a utility to copy a directory to multiple USB sticks. When Form1 loads, I would like for a label to display status "Detecting Disk Drives...", and then call a method to read the drives and populate the form with information. I have it working, except when the form loads it calls the method before displaying the label. Therefore it appears to be hung (the label is actually a white box on a gray background). I have tried timers and threads and everything I can think of, each with a different dead end. I have not yet found a way to have the label update before calling the method to read the drives.
The method getAndDisplayData() is wait 'hangs' my program. I would like for it not to be called until after the form has updated the text of lblDisplayStatus.Text
I also do not want the user to have to interact with the form before calling the method.
Here is my C# code:
private void USB_Utility_Load(object sender, EventArgs e)
{
lblDisplayStatus.Text = "Detecting Disk Drives...";
}
private void tabUSB_Prep_Enter(object sender, EventArgs e)
{
tabUSB_Prep.Controls.Clear();
getAndDisplayData();
}
Any help would be greatly appreciated.
Here is the code that I ended up with:
BackgroundWorker _worker;
private void USB_Utility_Load(object sender, EventArgs e)
{
_worker = new BackgroundWorker(); // Should be a field on the form.
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
lblDisplayStatus.Text = "Detecting Disk Drives...";
_worker.RunWorkerAsync();
}
//Background Worker
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblDisplayStatus.Text = "Done...";
displayData();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
getData();
}
The old-fashioned way would be to use a BackgroundWorker to run the blocking work in getAndDisplayData and then update the label before starting the worker and again when the worker completes.
Now-adays I assume you could also use tasks to get the exact same result, but I haven't actually tried it as WinForms is not often first choice for new projects.
BackgroundWorker _worker;
public void Form_Load(object sender, EventArgs e) {
_worker = new BackgroundWorker(); // Should be a field on the form.
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
lblDisplayStatus.Text = "Detecting Disk Drives...";
_worker.RunWorkerAsync();
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
lblDisplayStatus.Text = "Done...";
}
private void DoWork(object sender, DoWorkEventArgs e) {
getAndDisplayData();
}
About background workers
you can try this
private void USB_Utility_Load(object sender, EventArgs e)
{
lblDisplayStatus.Text = "Detecting Disk Drives...";
}
private void tabUSB_Prep_Enter(object sender, EventArgs e)
{
tabUSB_Prep.Controls.Clear();
Task<List<string>> t = new Task<List<string>>(DetectDrivesMethod());
t.ContinueWith((result)=>DisplayDrives(result.Result),TaskScheduler.FromCurrentSynchronizationContext);
t.Start();
}
You can tweak the code to fit your requirement. In DetectDriveMethod you will have logic to get data in background thread and in continue with, you can have logic to update UI. It is important that you pass syncronization context otherwise you will end up with Cross Thread exceptions.
If you want to use the new(ish) async/await pattern, you need to use the TaskScheduler to update the UI from the original thread. Here's an example:
// clear the form
tabUSB_Prep.Controls.Clear();
// This is just to show crossing a "context" works
string test = "";
// get the UI's current TaskScheduler
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
// This can be used to wrap a method that doesn't
// directly implement async/await
Task.Run(() =>
{
// Your method to GET the data (don't update the UI here)
test = "I can set a variable in this context!";
}).ContinueWith(task =>
{
if (task.Status == TaskStatus.RanToCompletion)
{
// update your UI here
// Again, this is just to show how crossing the context works
MessageBox.Show(test);
}
else
{
// update UI with an error message, or display a MessageBox?
}
}, scheduler);

Pass different method/function to DoWork event of Background worker

Please bear with my ignorance on this subject and on the terminology I have used. Please correct me where I am incorrect.
I have used a background Worker from the toolbox onto my form and I am passing a method to the DoWork event. What I have understood in this first attempt with background Worker is that I can use the background Worker that I've created only for 1 task. See code below:
private void btn1_Click(object sender, EventArgs e)
{
// Should call the uploadToDB1 using BackgroundWorker's DoWork event.
backgroundWorker1.RunWorkerAsync();
}
private void btn2_Click(object sender, EventArgs e)
{
// Should call the uploadToDB2 using BackgroundWorker's DoWork event.
backgroundWorker1.RunWorkerAsync();
}
private void uploadToDB1()
{
// Code for uploading to DB 1.
}
private void uploadToDB2()
{
// Code for uploading to DB 2.
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
uploadToDB1(); // I want to change this to uploadToDB2 i.e. a variable method, How do I assign a call to this?
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Visible = true;
progressBar1.Maximum = maxRecords;
lblProgress.Text = Convert.ToString(e.ProgressPercentage.ToString() + "/" + maxRecords);
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
lblProgress.Text = "";
}
I need to be able to dynamically pass a method to the DoWork event without having the need to create multiple background Workers as the actions in the rest of the events related to the background Worker remains unchanged.
Could you please advise how I should go about doing this?
Updated Code using TPL, however I am getting a cross thread error. Could you please help with a corrected code? Upload to DB 2 should happen only after upload to DB 1 is complete. So each time an upload happens the label and progress bar needs to be updated. I also need to pass different text to the label.
private void btn1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(uploadToDB1);
}
private void uploadToDB1()
{
for(i=0;i<dt.rows.count-1;i++)
{
// Code for inserting into DB1.
progressbar1.maximum=dt.rows.count-1;
progressbar1.value=i;
}
uploadToDB2();
}
private void uploadToDB2()
{
for(i=0;i<dt.rows.count-1;i++)
{
// Code for inserting into DB2.
progressbar1.maximum=dt.rows.count-1;
progressbar1.value=i;
}
}
What you can do, and is a bit of a hack, is pass an Action as an argument for invocation to your DoWorkAsync:
var bw = new BackgroundWorker();
bw.DoWork += (s, o) =>
{
Action actualWork = (Action)o.Argument;
actualWork();
}
and when you invoke DoWorkAsync:
Action action = () => DoSomething();
bw.RunWorkerAsync(action);
Instead, as #Sriram suggested, look into the Task Parallel Library, which will make you life a bit easier:
private async void btn1_Click(object sender, EventArgs e)
{
await Task.Run(UpdateFirst);
// Update UI here.
}
private async void btn2_Click(object sender, EventArgs e)
{
await Task.Run(UpdateSecond);
// Update UI again.
}
An extensive answer on TPL and the use of IProgess<T> can be found in How to provide a feedback to UI in a async method?

Close busy form when background-worker is complete?

I have an application that when busy will open a busy form (FormWaitingForm) to indicate to the user that the application is busy. How do i close FormWaitingForm in the event backgroundWorker1_RunWorkerCompletedbelow ?
private void radButtonCheckFiles_Click(object sender, EventArgs e)
{
var bw = new BackgroundWorker();
// define the event handlers
bw.DoWork += new DoWorkEventHandler(ProcessTickTemp);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
bw.RunWorkerAsync(); // starts the background worker
// execution continues here in parallel to the background worker
using (var FormWaitingForm = new WaitingForm()) //
{
var result = FormWaitingForm.ShowDialog();
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// How do i close FormWaitingForm here ?
}
You could try something like this. Retain a reference to the form outside of the click method and then open it non-modally (so that you don't have to wait for the user to close it).
WaitingForm formWaitingForm;
private void radButtonCheckFiles_Click(object sender, EventArgs e)
{
// background code here
formWaitingForm = new WaitingForm();
formWaitingForm.Show();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
formWaitingForm.Close();
formWaitingForm.Dispose();
}
You would have to add some code to handle if the user closes the waiting form without waiting for you to do it.
That said, the way I usually implement a waiting/progress-type form is to incorporate the background process into the progress form itself and show something like a progress bar.
This link might give you some more ideas.

Progressbar for long running function WP7 C#

I am writing a sudoku solver app. The calculation time of the solver in certain cases can exceed 3 seconds, which would require a progress bar.
so my code:
private void solveButton_Click(object sender, RoutedEventArgs e)
{
progressBar1.Visibility = Visibility.Visible;
progressBar1.IsIndeterminate = true;
mySolver.Solve(initialValue)
progressBar1.Visilibity=Visilibity.collapsed;
progressBar1.IsIndeterminate = false;
}
The code here is a condensed version of my actual code. This code doesn't work, as the progress bar does not appear at all. It seems to me that the UI updates only after event is finised executed. If I didn't hide the progressbar after the solver step, the progressBar appears after the sudoku is solved. Replacing the solver with thread.sleep(1000) also results in the same UI update.
thanks for your help.
You should start the solver on a separate thread. That way the user interface thread can keep working on user interface objects even during the solving process, which allows your progress bar to be drawn on the screen and updated.
Problem is that your UI thread is not getting free in between to display the progress bar
You need to use the background worker to solve the problem and in the main UI thread you should display the progress bar
private void solveButton_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += new DoWorkEventHandler(DoWork);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
bg.RunWorkerAsync();
progressBar1.Visibility = Visibility.Visible;
progressBar1.IsIndeterminate = true;
}
void DoWork(Object sender, DoWorkEventArgs args)
{
mySolver.Solve(initialValue)
}
void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs args)
{
// this method will be called once background worker has completed it's task
progressBar1.Visilibity=Visilibity.collapsed;
progressBar1.IsIndeterminate = false
}
Technically the code works fine, you just wrote the wrong code.
Your solver is running on the UI thread so it never has a chance to draw the progress bar before you hide it again. You need to spawn a thread (or use a background worker) to free up the UI thread so it can draw your progress bar.
I don't know too much about WP7 but with winforms if you have a long running function it needs to be run on a different thread than the UI.
Is BackgroundWorker available to you on WP7? you can update the bar on the ProgressChanged event and change the viability on the RunWorkerCompleted event
private void solveButton_Click(object sender, RoutedEventArgs e)
{
progressBar1.Visibility = Visibility.Visible;
progressBar1.IsIndeterminate = true;
solveButton.Enabled = false; //I reccomend this so the button can't be pressed twice.
BackgroundWoker bw = new BackgroundWorker();
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerAsync()
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
mySolver.Solve(initialValue, e)
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
{
//Handle any exceptions that happened in the background worker.
}
progressBar1.Visilibity=Visilibity.collapsed;
progressBar1.IsIndeterminate = false;
solveButton.Enabled = true;
((IDisposable)sender).Dispose();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
//inside mySolver
void Solve(somthing initialValue, DoWorkEventArgs e)
{
//Your solver work
e.ReportProgress(progress); //a int from 0-100
//more solver work
}
If you still want to run it on the UI Thread (which i wouldn't recommend!!) you can use the progressbar from the WP7 Toolkit that was released yesterday. It contains a progressbar that will work while your UI Thread is blocked.

Categories

Resources