This is the code that I am trying to execute, but stepping through my code I never see any progress indicated or updated on my windows form showing progressbar1. This is my 1st attempt in getting a background worker to function properly, and all I have is a windows form with one button on it and this is all of the code involved in the project.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private int i = 0;
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = false;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
ReadySteadyGo();
worker.ReportProgress((i * 10));
FinalizeAndFinish();
worker.ReportProgress((i * 10));
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Text = "Done!";
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Text = (e.ProgressPercentage.ToString() + "%");
}
private void ReadySteadyGo()
{
Thread.Sleep(100000);
}
private void FinalizeAndFinish()
{
Thread.Sleep(1000);
}
}
It appears that you are using Thread.Sleep() to simulate a long-running operation. There are a few things you should consider based on your code example:
When the backgroundWorker1.RunWorkerAsync(); is executed, it starts working on another thread. Thus, if you are debugging interactively and you have not set a breakpoint in the backgroundWorker1_DoWork method, you are not likely to see this code execute.
When the Thread.Sleep(100000) executes, it essentially means that the background worker will pause for 100 seconds - so you need to make sure you are waiting at least that long to see the UI updated.
Also, as per Hans Passant's comment, consider the following:
Nor can you see it doing anything, there's no point to assigning the
ProgressBar.Text property since it doesn't display text. Set Value
instead.
I recreated your example in Visual Studio and am hitting a breakpoint in backgroundWorker1_DoWork so the multi-threading is working properly, you just need to do proper processing?
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
Related
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 3 years ago.
I have a WindowsForm in C# with a TrayIcon including a contextMenuStrip with a ToolStripTextBox, a FileSystemWatcher and a BackgroundWorker.
The FileSystemWatcher throws an event when a new file is created which then starts the BackgroundWorker. The BackgroundWorker reports progress which updates a ToolStripTextBox on the TrayIcon in the ProgressChanged event handler.
Curiously this works fine as long as the ToolStripMenu has not been visible since the start of the program. As soon as I right-click on the TrayIcon to show the ToolStripMenu (regardless of the BackgroundWorker being idle or not), the ToolStripTextBox starts to throw an InvalidOperationException (invalid cross-thread operation) every time I try to update its .Text property.
When I start the BackgroundWorker from a button click event it all works fine, too. I can see the ToolStripTextBox updating.
What is different when I start the BackgroundWorker from the FileSystemWatcher event? Or rather what is different after showing the contextMenuStrip? Does the contextMenuStrip belong to another thread after showing?
I might find another way to show the progress instead of the ToolStripTextBox but I am curious to know what causes this. I'd be very glad if you could help.
Minimal code example below.
public partial class Form1 : Form
{
FileSystemWatcher watcher = new FileSystemWatcher();
string watcherpath = #"C:\Temp\files";
BackgroundWorker bgw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
watcher.Path = watcherpath;
watcher.Created += Watcher_Created;
watcher.EnableRaisingEvents = true;
bgw = new BackgroundWorker();
bgw.DoWork += Bgw_DoWork;
bgw.RunWorkerCompleted += Bgw_RunWorkerCompleted;
bgw.WorkerSupportsCancellation = true;
bgw.WorkerReportsProgress = true;
bgw.ProgressChanged += Bgw_ProgressChanged;
}
private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
toolStripTextBox1.Text = $"Progress: {e.ProgressPercentage}%";
}
private void Bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done!");
}
private void Bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 10; i++)
{
Thread.Sleep(1000);
(sender as BackgroundWorker).ReportProgress(i * 10);
}
}
private void Watcher_Created(object sender, FileSystemEventArgs e)
{
File.Delete(e.FullPath);
bgw.RunWorkerAsync();
}
private void toolStripMenuItem1_Click(object sender, EventArgs e)
{
this.Close();
}
private void notifyIcon1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
contextMenuStrip1.Show(Cursor.Position);
}
private void button1_Click(object sender, EventArgs e)
{
bgw.RunWorkerAsync();
}
}***
Please try below code
private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
SetToolStripText(e.ProgressPercentage)
}
private void SetToolStripText(object ProgressPercentage)
{
this.BeginInvoke((MethodInvoker)delegate { this.toolStripTextBox1.Text = $"Progress: {e.ProgressPercentage}%";
}
When the user tries to double click button, the backgroundWorker will initiate twice.
is this a good workaround?
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Some processing here
Thread.Sleep(1000);
button1.Invoke((MethodInvoker)delegate { button1.Enabled= true; });
}
// Run backgroundProcess
private void button1_Click(object sender, EventArgs e)
{
this.button1.Enabled = false;
backgroundWorker1.CancelAsync();
backgroundWorker1.RunWorkerAsync();
}
You can use backgroundWorker1_RunWorkerCompleted event to enable button1. instead for enabling then in backgroundWorker1_DoWork
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.button1.Enabled = true;
//rest of operations
}
What is wrong with your code:
You ware enabling the button in _DoWork that is meaning less since backgroundWorker1.RunWorkerAssync(); will call _DoWork() there the button enables and hence the second click also get executed.
You have to take care of three basic things when you are going to use BackgrounWorker.
DoWork: You can handle execution of your business logic and cancellation of your work if required.
RunWorkerCompleted: You can handle post execution task. Here you want to enable button, so you should write appropriate code into RunWorkerCompleted Event.
Handle the WorkerSupportsCancellation property if you want to cancel backgroundworker process at any point of execution.
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
button1.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Invoke((MethodInvoker)delegate { button1.Enabled = true; });
backgroundWorker1.DoWork -= backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted -= backgroundWorker1_RunWorkerCompleted;
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
AppendTextBox( "Hit: " + i.ToString() + Environment.NewLine);
if (backgroundWorker1.CancellationPending)
{
break;
}
}
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
}
}
public void AppendTextBox(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(AppendTextBox), new object[] { value });
return;
}
textBox1.Text += value;
}
private void button2_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy)
backgroundWorker1.CancelAsync();
}
If the worker is actually running, then almost certainly not.
Cancellation is cooperative. Calling CancelAsync just sets the CancellationPending flag to true. It is up to the background worker to periodically check this flag and respond to cancellation.
The chances that a running worker has gotten to an appropriate point to check that flag, checked it, and responded, all within the gap of time between these two lines of code:
backgroundWorker1.CancelAsync();
backgroundWorker1.RunWorkerAsync();
Is extremely slim. It's more likely (if the worker was running) that RunWorkerAsync will throw InvalidOperationException since the worker is still running.
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.
What I want is when some method is doing some task UI keeps itself active and I want to show the progress of the work in a progress-bar.
I have a method, a BackGroundWorker and a Progressbar. I want to call the method when BackGroundWorker starts running and show the progress. The method contains a loop. So, it can report the progress.
So, what can be done?
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'dataSet1.TBLMARKET' table. You can move, or remove it, as needed.
myBGWorker.WorkerReportsProgress = true;
}
private void myBGWorker_DoWork(object sender, DoWorkEventArgs e)
{
parseFiles();
}
private void myBGWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
}
private void myBGWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done");
}
private void parseButton_Click(object sender, EventArgs e)
{
myBGWorker.RunWorkerAsync();
}
public void parseFiles()
{
for()
{
//parsing
myBGWorker.ReportProgress(...);
}
}
But it's not working. The Progressbar is not updating. Only a small progress is showing after the MessageBox "Done".
Instead of using one ParseFiles method (which should depend on myBGWorker) use loop and method which parse one file. Report progress percentage in that loop:
private void parseButton_Click(object sender, EventArgs e)
{
parseButton.Enabled = false;
myBGWorker.RunWorkerAsync();
}
private void myBGWorker_DoWork(object sender, DoWorkEventArgs e)
{
for(int i = 0; i < filesCount; i++)
{
ParseSingleFile(); // pass filename here
int percentage = (i + 1) * 100 / filesCount;
myBGWorker.ReportProgress(percentage);
}
}
void myBGWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
}
void myBGWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
parseButton.Enabled = true;
MessageBox.Show("Done");
}
To. soham.m17
using with sender argument
private void myBGWorker_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
for(int i = 0; i < filesCount; i++)
{
ParseSingleFile(); // pass filename here
int percentage = (i + 1) * 100 / filesCount;
worker.ReportProgress(percentage); // use not myBGWorker but worker from sender
}
}
I am sorry about the question. Actually the code works fine. It was not showing the Progressbar as the argument in myBGWorker.ReportProgress() was fraction and not percentage. So, it was not showing it. Sorry for the inconvenience.
Moderator may delete this thread. Otherwise it can be a tutorial for others.
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";
}
}