WPF ProgressBar - make the progress bar completed after the operation finished - c#

I have a WPF form which runs a background operation with progress bar. but the problem is that;
when the operation is completed, the progress bar is still running. I mean it shows like the operation is in progress.
how can I stop that? here is my whole code;
System.ComponentModel.BackgroundWorker mWorker;
private void button1_Click(object sender, RoutedEventArgs e) {
mWorker = new System.ComponentModel.BackgroundWorker();
mWorker.DoWork +=new System.ComponentModel.DoWorkEventHandler(worker_DoWork);
mWorker.ProgressChanged +=new System.ComponentModel.ProgressChangedEventHandler(worker_ProgressChanged);
mWorker.WorkerReportsProgress = true;
mWorker.WorkerSupportsCancellation = true;
mWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync();
while (pbProcessing.Value != 100) {
if (!mWorker.CancellationPending) {
try {
pbProcessing.Value = (pbProcessing.Value + 0.01) % 100;
} catch (System.Exception ex) {
// No action required
}
} else {
MessageBox.Show(this, "Process cancelled", "Cancel Process", MessageBoxButton.OK);
break;
}
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background,
new System.Threading.ThreadStart(delegate { }));
}
}
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
// Do your work here, its on seperate thread
System.Threading.Thread.Sleep(10000);
}
private void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) {
pbProcessing.Value = e.ProgressPercentage;
}
private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) {
// Stop Progressbar updatation
Window1 w = new Window1();
w.Browser.Navigate(new Uri("http://stackoverflow.com"));
w.Show();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
if (mWorker != null) {
if (mWorker.IsBusy) {
mWorker.CancelAsync();
}
}
}

If you want to hide the progressbar after the work is done, set its Visibility property to Visibility.Hidden. If you just want to reset it to its initial state, set it's Value to 0 (or to pbProgressing.Minimum, if you changed that from its default value).
As a side note, your code doesn't really make sense: Instead of continuously changing pbProcessing.Value in the button event handler (which is completely useless, since no UI updates are performed until the button event handler has completed), you should only change the value in ProgressChanged. I.e., your code should look something like this:
System.ComponentModel.BackgroundWorker mWorker;
private void button1_Click(object sender, RoutedEventArgs e) {
mWorker = new System.ComponentModel.BackgroundWorker();
mWorker.DoWork +=new System.ComponentModel.DoWorkEventHandler(worker_DoWork);
mWorker.ProgressChanged +=new System.ComponentModel.ProgressChangedEventHandler(worker_ProgressChanged);
mWorker.WorkerReportsProgress = true;
mWorker.WorkerSupportsCancellation = true;
mWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync();
// Don't do anything else here
}
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
for (int i = 1; i < 100; i++) {
mWorker.ReportProgress(i);
// Do some part of the work
System.Threading.Thread.Sleep(100);
// Check if the user wants to abort
if (mWorker.CancellationPending) {
e.Cancel = true;
return;
}
}
mWorker.ReportProgress(100); // Done
}
private void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) {
pbProcessing.Value = e.ProgressPercentage;
}
private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) {
// Stop Progressbar updatation
Window1 w = new Window1();
w.Browser.Navigate(new Uri("http://stackoverflow.com"));
w.Show();
// Check the result
if (e.Cancelled) {
// show the message box that the task has been canceled
}
// Reset Progress bar
pbProcessing.Value = 0;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
if (mWorker != null) {
if (mWorker.IsBusy) {
mWorker.CancelAsync();
}
}
}

Related

How to update a listbox dynamically?

I am making a search utility and using a BackgroundWorker to search. I want that as soon as the first result is found , a new window should open up with a ListBox with the first element displayed. Now, I want that as soon as subsequent results are found, the ListBox should be updated with those results.
The method thought by me was to report the progress as soon as results are found and pass "New" and "Update" as userState to the method.
Based on the userState, I can decide whether to create a new Form or update and existing one.
Here is the code :-
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.UserState.ToString() == "New")
{
Form ResultForm = new Form();
ResultForm.Name = "ResultForm" + i.ToString();
LastFormName = ResultForm.Name;
ListBox ResultListBox = new ListBox();
ResultListBox.DataSource = SearchList;
ResultListBox.Name = "ResultListBox" + i.ToString();
LastListName = ResultListBox.Name ;
ResultForm.Container.Add(ResultListBox);
ResultListBox.Show();
ResultForm.Show();
i++;
}
else
{
;
}
}
I have stored the names of the Last open Form and it's ListBox in the variables LastFormName and 'LastListName'.
I am unable to understand what to put in the else condition, so as to update the ListBox.
What I would do is expose some properties on the popup form so that you can tell if it is open and have access to the list box.
public partial class Popup : Form
{
public bool isOpen;
public ListBox PopupListBox;
public Popup()
{
InitializeComponent();
}
void Popup_FormClosing(object sender, FormClosingEventArgs e)
{
isOpen = false;
}
private void Popup_Load(object sender, EventArgs e)
{
this.FormClosing += Popup_FormClosing;
PopupListBox = popupListBox;
}
}
Then on the calling form I would subscribe to the ProcessedChanged Event and update the listbox with the data you are passing through the ProcessedChangedEventArgs. Here is the code for the calling form
public partial class Form1 : Form
{
Popup popupForm = new Popup();
BackgroundWorker backgroundWorker = new BackgroundWorker();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork += backgroundWorkerDoWork;
backgroundWorker.ProgressChanged += backgroundWorkerProgressChanged;
backgroundWorker.RunWorkerCompleted += backgroundWorkerRunWorkerCompleted;
}
void backgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
//What do you want to do if Cancelled?
}
else if (!(e.Error == null))
{
//What do you want to do if there is an error?
}
else
{
//What do you want to do when it is done?
}
}
void backgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (!popupForm.isOpen || popupForm == null)
{
popupForm = new Popup();
popupForm.Show();
popupForm.isOpen = true;
}
else
{
popupForm.Activate();
popupForm.WindowState = FormWindowState.Normal;
}
popupForm.PopupListBox.Items.Add(e.ProgressPercentage.ToString() + "%");
}
void backgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10));
}
}
}
private void buttonStart_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy != true)
{
backgroundWorker.RunWorkerAsync();
}
}
private void buttonCancel_Click(object sender, EventArgs e)
{
if (backgroundWorker.WorkerSupportsCancellation == true)
{
backgroundWorker.CancelAsync();
}
}
}
You shouldn't be doing work in the ProgressChanged event handler
You won't have access to the results as it is only passed an int for progress and user state
This is on the UI thread. The entire point is to do your processing on a background thread
The name DoWork event handler is clear that this is where you should do your processing.
In answer to your question. Since you create the ListBox in your event handler it goes out of scope outside the if statement. You need to create this in a more global scope. Then to add to it ResultListBox.Items.Add("ResultListBox" + i.ToString());

Resume thread and work on another button in the meantime

I am new in C#. I found some code which work on progressbar. What is does, when someone click on button start btnStartAsyncOperation_Click the progress bar starts increasing and when btnCancel_Click is pressed it cancel the operation. Here is the code
namespace BackgroundWorkerSample
{
public partial class Form1 : Form
{
BackgroundWorker m_oWorker;
public Form1()
{
InitializeComponent();
m_oWorker = new BackgroundWorker();
m_oWorker.DoWork += new DoWorkEventHandler(m_oWorker_DoWork);
m_oWorker.ProgressChanged += new ProgressChangedEventHandler(m_oWorker_ProgressChanged);
m_oWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(abcd);
m_oWorker.WorkerReportsProgress = true;
m_oWorker.WorkerSupportsCancellation = true;
}
void abcd(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
lblStatus.Text = "Task Cancelled.";
}
else if (e.Error != null)
{
lblStatus.Text = "Error while performing background operation.";
}
else
{
lblStatus.Text = "Task Completed...";
}
btnStartAsyncOperation.Enabled = true;
btnCancel.Enabled = false;
}
void m_oWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
lblStatus.Text = "Processing......" + progressBar1.Value.ToString() + "%";
}
void m_oWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
m_oWorker.ReportProgress(i);
if (m_oWorker.CancellationPending)
{
e.Cancel = true;
m_oWorker.ReportProgress(0);
return;
}
}
//Report 100% completion on operation completed
m_oWorker.ReportProgress(100);
}
private void btnStartAsyncOperation_Click(object sender, EventArgs e)
{
btnStartAsyncOperation.Enabled = false;
btnCancel.Enabled = true;
//Start the async operation here
m_oWorker.RunWorkerAsync();
}
private void btnCancel_Click(object sender, EventArgs e)
{
if (m_oWorker.IsBusy)
{
//Stop/Cancel the async operation here
m_oWorker.CancelAsync();
}
}
private void progressBar1_Click(object sender, EventArgs e)
{
}
private void lblStatus_Click(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
}
}
}
Now I added 2 more button, button1 to pause and button2 to resume. Since I could not find any method to resume, I had to use CancelAsync() function when I press pause and I keep the value of progress bar in a global variable. Then when I press resume I start the progress bar again using RunWorkerAsync. But the problem is, I can not send the value of global variable in this function so it start from 0 progress.
I tried to use thread.sleep(infinite time here) when someone press pause and then stop the thread when someone press resume. Still the problem is, I can not press any button in this situation. Still if I enable button they don't work.
Please give me some solution.
You could try having your own variable, i.e
bool isPaused = false;
When someone clicks your pause button...
isPaused = true;
And set it to false when someone clicks resume. Finally, in your for loop in your doWork method, make it wait until that variable is false.
while (isPaused)
{
Thread.Sleep(100);
}
Let me know how this works out for you.

How to pause and resume a BackgroundWorker?

This is how I did it in my code:
In the backgroundWorker DoWork event I did:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
_busy.WaitOne();
this.Invoke(new MethodInvoker(delegate { label2.Text = "Website To Crawl: "; }));
this.Invoke(new MethodInvoker(delegate { label4.Text = mainUrl; }));
webCrawler(mainUrl, levelsToCrawl, e);
}
Then in the pause button click event I did:
private void button4_Click(object sender, EventArgs e)
{
_busy.Reset();
}
In the resume button click event I did:
private void button5_Click(object sender, EventArgs e)
{
_busy.Set();
}
But it's not working when I click to start the process:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
button1.Enabled = false;
this.Text = "Processing...";
label6.Text = "Processing...";
label6.Visible = true;
button2.Enabled = false;
checkBox1.Enabled = false;
checkBox2.Enabled = false;
numericUpDown1.Enabled = false;
button3.Enabled = true;
}
Nothing happen only when I click the resume button the process start then when I click the pause button nothing happen.
I want that when I click the start process button it will start the backgroundWorker regular then when clicking the pause button it will pause and the resume button it will resume.
What did I do wrong ? Can someone fix my code ?
In your BackgroundWorker thread code, you need to find places that are safe to "pause" execution. The ManualResetEvent is the right way to code. This other post might help:
Is there a way to indefinitely pause a thread?
Basically, in a few choice points in your worker thread code where you want to allow it to pause, try inserting:
_busy.WaitOne(Timeout.Infinite);
And when you want to pause (from your main thread) you use:
_busy.Reset();
And to resume:
_busy.Set();
You should be able to do this using the ManualResetEvent like this ...
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
_busy.WaitOne();
test(mainUrl, levelsToCrawl, e);
}
... and then when you want to pause the thread call _busy.Reset() ... and when you want to restart it call _busy.Set().
Additionally, you can place _busy.WaitOne(); anywhere you want to pause.
I've been looking for the answer of this thread but I come up with my own solution i made and i just wanna share it with you. hope this works.
I have a background worker and i want to pause it when i hit close button of my form. asking "You are about to cancel the process" so it should pause the process.
declare bool pauseWorker = false; on your class.
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (condition)
{
if (pauseWorker == true)
{
while (pauseWorker == true)
{
if (pauseWorker == false) break;
}
}
else
{
//continue process... your code here
}
}
}
private void frmCmsnDownload_FormClosing(object sender, FormClosingEventArgs e)
{
if (bgWorker.IsBusy)
{
pauseWorker = true; //this will trigger the dowork event to loop that
//checks if pauseWorker is set to false
DiaglogResult x = MessageBox.Show("You are about cancel the process", "Close", MessageBoxButtons.YesNo);
if (x == DialogResult.Yes) bgWorker.CancelAsync();
else
{
e.Cancel = true;
pauseWorker = false; //if the user click no
//the do work will continue the process
return;
}
}
}
Therefore the main solution here is the boolean declaration that controls the DoWork event of BGWorker.
Hope this solution helps your problem. Thank you.
I use a simple class that utilizes System.Thread.Monitor and lock()...
public class ThreadPauseState {
private object _lock = new object();
private bool _paused = false;
public bool Paused {
get { return _paused; }
set {
if(_paused != value) {
if(value) {
Monitor.Enter(_lock);
_paused = true;
} else {
_paused = false;
Monitor.Exit(_lock);
}
}
}
}
public void Wait() {
lock(_lock) { }
}
}
Using it is very simple...
private ThreadPauseState _state = new ThreadPauseState();
private void btnPause_Click(object sender, EventArgs e) {
_state.Paused = true;
}
private void btnResume_Click(object sender, EventArgs e) {
_state.Paused = false;
}
private void btnCancel_Click(object sender, EventArgs e) {
backgroundWorker1.CancelAsync();
_state.Paused = false; // needed if you cancel while paused
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
var worker = (BackgroundWorker)sender;
for(var _ = 0; _ < 100; _++) {
_state.Wait();
if(worker.CancellationPending) return;
Thread.Sleep(100); // or whatever your work is
}
}
This works for me:
bool work = true;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += myChangeFunction;
backgroundWorker1.RunWorkerAsync();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true && work)
{
// Your code here
backgroundWorker1.ReportProgress(0);
Thread.Sleep(1000);
}
e.Cancel = true;
}
private void myChangeFunction(object sender, ProgressChangedEventArgs e)
{
// Here you can change label.text or whatever thing the interface needs to change.
}
private void Stop()
{
work = false;
}
private void Start()
{
work = true;
backgroundWorker1.RunWorkerAsync();
}
NOTE: If you want to change something of the interface, you have to put it in the myChangeFunction(), because in the DoWork() function will not work. Hope this helps.

Background Worker Issue

Edit: I solved the problem by creating the background worker by creating it in code instead of dragging and dropping in design.
Now I know how to use a background worker.
Question
It's my first time using a BGWorker, so here's my issue...
The cursor doesn't change to "Wait".
The progress bar doesn't update.
RunWorkerCompleted isn't invoked.
But the textbox does update.
Am I doing something wrong ?
The Code
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace BGWorker
{
public partial class Main : Form
{
public Main()
{
InitializeComponent();
progressBar1.Minimum = 0;
progressBar1.Value = 0;
progressBar1.Maximum = 100;
}
private void button1_Click(object sender, EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
textBox1.AppendText(i.ToString());
textBox1.AppendText("\n");
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
label1.Text = e.Error.Message;
}
else
{
label1.Text = "All Done !";
Cursor.Current = Cursors.Default;
}
}
}
}
Thanks.
I agree with pst.
Always create your worker in the code behind - dragging and dropping on the form is never a good idea
You never access UI elements from the DoWork. You can only do this from the ReportProgress and RunWorker Completed Events
Your if statement segment should contain your most common path with the least common path in the else.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 100; i++)
{
sb.AppendLine(i.ToString());
backgroundWorker1.ReportProgress(i);
}
e.Result = sb.ToString();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
textbox1.Text = e.Result.ToString();
label1.Text = "All Done !";
Cursor.Current = Cursors.Default;
}
else
{
label1.Text = e.Error.Message;
}
}

How to use a BackgroundWorker?

I know it has 3 methods. In my program I have a method to send a message. It is often late and the program sometimes doesn't send the message at all in response to a button press. At times it is as late as 5 seconds from what I would expect and the program freezes. I want to use a BackgroundWorker to send the message as expected and allow the program to run normally at all times. I had the code for sending the message in a button handler. Now where do I put this equivalent code? I would like all of this to still be handled by a button press.
Is this the appropriate handler?
backgroundWorker1.RunWorkerAsync();
and in:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {}
I'm going to put my code in the button handler?
And this before:
carga.progressBar1.Minimum = 0;
carga.progressBar1.Maximum = 100;
Carga is my other form where the ProgressBar is. How do I use a BackgroundWorker in this scenario?
You can update progress bar only from ProgressChanged or RunWorkerCompleted event handlers as these are synchronized with the UI thread.
The basic idea is. Thread.Sleep just simulates some work here. Replace it with your real routing call.
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
I know this is a bit old, but in case another beginner is going through this, I'll share some code that covers a bit more of the basic operations, here is another example that also includes the option to cancel the process and also report to the user the status of the process. I'm going to add on top of the code given by Alex Aza in the solution above
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted; //Tell the user how the process went
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true; //Allow for the process to be cancelled
}
//Start Process
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
//Cancel Process
private void button2_Click(object sender, EventArgs e)
{
//Check if background worker is doing anything and send a cancellation if it is
if (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
}
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
backgroundWorker1.ReportProgress(i);
//Check if there is a request to cancel the process
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
backgroundWorker1.ReportProgress(0);
return;
}
}
//If the process exits the loop, ensure that progress is set to 100%
//Remember in the loop we set i < 100 so in theory the process will complete at 99%
backgroundWorker1.ReportProgress(100);
}
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
lblStatus.Text = "Process was cancelled";
}
else if (e.Error != null)
{
lblStatus.Text = "There was an error running the process. The thread aborted";
}
else
{
lblStatus.Text = "Process was completed";
}
}

Categories