I have a process that takes a long time, and I want a window to show the progress. But, I can't figure how to display the progress.
Here's the code:
if (procced)
{
// the wpf windows :
myLectureFichierEnCour = new LectureFichierEnCour(_myTandemLTEclass);
myLectureFichierEnCour.Show();
bgw = new BackgroundWorker();
bgw.DoWork += startThreadProcessDataFromFileAndPutInDataSet;
bgw.RunWorkerCompleted += threadProcessDataFromFileAndPutInDataSetCompleted;
bgw.RunWorkerAsync();
}
And:
private void startThreadProcessDataFromFileAndPutInDataSet(object sender, DoWorkEventArgs e)
{
_myTandemLTEclass.processDataFromFileAndPutInDataSet(
_strCompositeKey,_strHourToSecondConversion,_strDateField);
}
I can call _myTandemLTEclass.processProgress to get a hint of the progress.
You should handle the ProgressChanged event and update the progress bar in your user interface there.
In the actual function that does the work (DoWork event handler), you'll call the ReportProgress method of the BackgroundWorker instance with an argument specifying the amount of task completed.
The BackgroundWorker example in MSDN Library is a simple code snippet that does the job.
Your backgroundWorker thread needs to handle the DoWork method and ProgressChanged.
You also need to make sure you turn on the WorkerReportsProgress flag to true (off by default).
See example code:
private void downloadButton_Click(object sender, EventArgs e)
{
// Start the download operation in the background.
this.backgroundWorker1.RunWorkerAsync();
// Disable the button for the duration of the download.
this.downloadButton.Enabled = false;
// Once you have started the background thread you
// can exit the handler and the application will
// wait until the RunWorkerCompleted event is raised.
// Or if you want to do something else in the main thread,
// such as update a progress bar, you can do so in a loop
// while checking IsBusy to see if the background task is
// still running.
while (this.backgroundWorker1.IsBusy)
{
progressBar1.Increment(1);
// Keep UI messages moving, so the form remains
// responsive during the asynchronous operation.
Application.DoEvents();
}
}
Related
I have a method that has a for loop in it. In the for loop I want to update some label's text on the mainform, but the changes are only done after the loop ends.
I tried to do it on another thread like this:
Thread firstthread = new Thread(new ThreadStart(myMethod));
firstthread.Start();
When I did that I got an InvalidOperationException because of trying to access controls on another thread or something like that.
How should I update the labels(or other controls) on the mainform from a loop while the loop is in progress?
You should use a BackgroundWorker. Place your long running loop inside of the DoWork event handler; it will run in a background thread and not block the UI thread. You can set ReportProgress to true and then attach an event handler to that to allow you to update a label (or whatever else) periodically. The ProgressReported event runs in the UI thread. You can also add a handler to the Completed event which runs in the UI thread as well.
You can look at the MSDN page for BackgroundWorker for details and code samples.
You should check the Invoke and BeginInvoke methods on the Form (for Windows.Forms) or on the Dispatcher object of the window (for WPF).
For example:
this.BeginInvoke(new Action(() => this.Text = "ciao"));
changes the title bar of the form.
BeginInvoke is asynchronous - it doesn't wait for the change to happen - while Invoke is synchronous and blocks until the change is done. Unless you have specifically that need, I would suggest using BeginInvoke which reduces the chances of an accidental deadlock.
This will allow you to update UI from a concurrent thread - and works whatever threading mechanism you are using (TPL tasks, plain Thread, etc.).
As Servy said, you can use something like in this simple example:
public partial class Form1 : Form
{
BackgroundWorker bgw;
public Form1()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
bgw.WorkerReportsProgress = true;
}
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string text = (string)e.UserState;
SetValue(text);//or do whatever you want with the received data
}
void SetValue(string text)
{
this.label1.Text = text;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10000; i++)
{
string text = "Value is " + i.ToString();
bgw.ReportProgress(1, text);
Thread.Sleep(1000);
}
}
private void button1_Click(object sender, EventArgs e)
{
bgw.RunWorkerAsync();
}
}
I am working on a WPF application. I have a time consuming method that I want to run async via BackgroundWorker. While the method runs, I want to display a modal "Please Wait..." dialog window, which must automatically close when the BackgroundWorker completes.
I currently have very little experience with BackgroundWorker or any multi threaded programming.
The code below currently results in an InvalidOperationException, with the message "The calling thread must be STA, because many UI components require this."
Please advise me on how to achieve what I am trying to achieve, and extra brownie-points if you can help me understand what is going wrong.
Many thanks!
EDIT
Just to clarify - The idea is that the main thread launches the BackgroundWorker, then shows the modal dialog. When the worker completes, it closes the modal dialog. When the modal dialog closes, the main thread continues.
public class ImageResizer
{
private BackgroundWorker worker;
private MemoryStream ImageData { get; set; } // incoming data
private public MemoryStream ResizedImageData { get; private set; } // resulting data
private Dialogs.WorkInProgressDialog ProgressDialog;
// Public interface, called by using class:
public MemoryStream ReduceImageSize(MemoryStream imageData)
{
// injected data:
this.ImageData = imageData;
// init progress dialog window:
ProgressDialog = new Dialogs.WorkInProgressDialog();
// Start background worker that asyncronously does work
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
// Show progress dialog. Dialog is MODAL, and must only be closed when resizing is complete
ProgressDialog.ShowDialog(); // THIS LINE CAUSES THE INVALID OPERATION EXCEPTION
// This thread will only continue when ProgressDialog is closed.
// Return result
return ResizedImageData;
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// Call time consuming method
ResizedImageData = ReduceImageSize_ActualWork();
}
// The actual work method, called by worker_DoWork
private MemoryStream ReduceImageSize_ActualWork()
{
// Lots of code that resizes this.ImageData and assigns it to this.ResizedImageData
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Async work completed - close progress dialog
ProgressDialog.Close();
}
}
You can't call ShowDialog from the BackgroundWorker. You have to use the Dispatcher to ask the UI thread to execute it:
this.Dispatcher.BeginInvoke(new Action(() => ProgressDialog.ShowDialog()));
The 'Completed' event of the BackgroundWorker is executed in the UI thread, so this part should be fine.
I'm trying to use a Background Worker in a WPF application. The heavy lifting task uses WebClient to download some HTML and parse some info out of it. Ideally I want to do that downloading and parsing without locking the UI and placing the results in the UI once it's done working.
And it works fine, however, if I quickly submit the "download and parse" command, I get the error:
This BackgroundWorker is currently busy and cannot run multiple tasks
concurrently
So I did some Googling and it seems that I can enable the .WorkerSupportsCancellation property of the background worker and just .CancelAsync(). However, this doesn't work as expected (canceling the current download and parse).
I still get the above error.
Here's my code:
//In window constructor.
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
//Declared at class level variable.
BackgroundWorker _backgroundWorker = new BackgroundWorker();
//This is the method I call from my UI.
private void LoadHtmlAndParse(string foobar)
{
//Cancel whatever it is you're doing!
_backgroundWorker.CancelAsync();
//And start doing this immediately!
_backgroundWorker.RunWorkerAsync(foobar);
}
POCOClassFoo foo = new POCOClassFoo();
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//This automagically sets the UI to the data.
Foo.DataContext = foo;
}
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//DOING THE HEAVY LIFTING HERE!
foo = parseanddownloadresult()!
}
Calling CancelAsync will still fire the RunWorkerCompleted event. In this event, you need to make sure that CancelAsync has not been called, by checking e.Cancelled. Until this event fires, you cannot call RunWorkerAsync.
Alternatively, I would recommend you do what Tigran suggested and create a new BackgroundWorker each time.
Further more, I would recommend storing the results of_backgroundWorker_DoWork in e.Result, then retrieve them from the same in _backgroundWorker_RunWorkerCompleted
Maybe something like this
BackgroundWorker _backgroundWorker;
private BackgroundWorker CreateBackgroundWorker()
{
var bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.DoWork += _backgroundWorker_DoWork;
bw.RunWorkerCompleted += new _backgroundWorker_RunWorkerCompleted;
return bw.
}
private void LoadHtmlAndParse(string foobar)
{
//Cancel whatever it is you're doing!
if (_backgroundWorer != null)
{
_backgroundWorker.CancelAsync();
}
_backgroundWorker = CreateBackgroundWorker();
//And start doing this immediately!
_backgroundWorker.RunWorkerAsync(foobar);
}
//you no longer need this because the value is being stored in e.Result
//POCOClassFoo foo = new POCOClassFoo();
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//Error handling goes here.
}
else
{
if (e.Cancelled)
{
//handle cancels here.
}
{
//This automagically sets the UI to the data.
Foo.DataContext = (POCOClassFoo)e.Result;
}
}
private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//DOING THE HEAVY LIFTING HERE!
e.Result = parseanddownloadresult()!
}
The thing is that CancelAsync() does what it climes: cancel in async way. That means that it will not stop immediately, but after some time. That time can never be calculated or predicted, so you have a couple of options:
Wait until this backround worker stops really, by waiting in cycle until IsBusy property of it becomes false
Or, I think, better solution is to start another background worker, considering that request of cancelation was already sent to the first one, so it will be soon or later stop. In this case, you need to know from which background worker data comes, in order to process it or not, cause on start of second the first one will still run and pump the data from WebService.
Hope this helps.
CancelAsync returns before the worker cancels and stops its work. Hence, your RunWorkerAsync call is starting before the worker is ready, and you're getting that error. You'll need to wait for the worker to be ready first.
When I'm not interested in tracking progress of an async operation, I tend to prefer to just slap a lambda at ThreadPool.QueueUserWorkItem instead of instantiating and setting up a background worker that I have to check the state of to be able to reuse in a sane way.
You need to verify before you kicks in.
f( !bw.IsBusy )
bw.RunWorkerAsync();
else
MessageBox.Show("Can't run the bw twice!");
You are calling CancelAsync without waiting for the background worker to actually cancel the work. Also you must have your own logic for cancelling the work. There is a good example on MSDN which shows how to do it. Basically in your parseanddownloadresult() method you need to check the CancellationPending property.
Hi i have stepped into some problem related to timer.
hope somebody can help..
I have a windows form containing a button
when i click on that button i start a parameterised thread
Thread thread1 = new Thread(new ParameterizedThreadStart( execute2));
thread1.Start(externalFileParams);
the code inside the thread executes very well
at the last line of this thread i start a timer
.
public void execute2(Object ob)
{
if (ob is ExternalFileParams)
{
if (boolean_variable== true)
executeMyMethod();//this also executes very well if condition is true
else
{
timer1.enabled = true;
timer1.start();
}
}
}
}
5 but the tick event of the timer is not fired
I am working on VS2008 3.5 framework. I have dragged the timer from toolbox and set its Interval to 300 also tried to set Enabled true/false
method is timer1_Tick(Object sender , EventArgs e) but its not fired
can anybody suggest what I am doing wrong?
You could try to start the timer this way:
Add in form constructor this:
System.Timers.Timer aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
// Set the Interval to 1 second.
aTimer.Interval = 1000;
Add this method to Form1:
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
//do something with the timer
}
On button click event add this:
aTimer.Enabled = true;
This timer is already threaded so no need to start a new thread.
It is true what MatÃas Fidemraizer says. But, there is a work around...
When you have a control on your form that is invokable (eg. a statusbar), just invoke that one!
C# Code sample:
private void Form1_Load(object sender, EventArgs e)
{
Thread sampleThread = new Thread(delegate()
{
// Invoke your control like this
this.statusStrip1.Invoke(new MethodInvoker(delegate()
{
timer1.Start();
}));
});
sampleThread.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
MessageBox.Show("I just ticked!");
}
System.Windows.Forms.Timer works in a single-threaded application.
Check this link:
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
Remarks says:
A Timer is used to raise an event at
user-defined intervals. This Windows
timer is designed for a
single-threaded environment where UI
threads are used to perform
processing. It requires that the user
code have a UI message pump available
and always operate from the same
thread, or marshal the call onto
another thread.
Read more "Remarks" section and you'll find that Microsoft recommends that you use this timer synchronizing it with the UI thread.
I would use a BackgroundWorker (instead of a raw thread). The main thread would subscribe to the worker's RunWorkerCompleted event: The event fires in your main thread when the thread completes. Use this event handler to restart your timer.
When my C# application closes it sometimes gets caught in the cleanup routine. Specifically, a background worker is not closing. This is basically how I am attempting to close it:
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
backgroundWorker1.CancelAsync();
while (backgroundWorker1.IsBusy) ; // Gets stuck here.
}
Is there a different way that I should be doing this? I am using Microsoft Visual C# 2008 Express Edition. Thanks.
ADDITIONAL INFORMATION:
The background worker does not appear to be exiting. This is what I have:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!backgroundWorker1.CancellationPending)
{
// Do something.
}
}
I've also modified the cleanup code:
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
while (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
System.Threading.Thread.Sleep(1000);
}
}
Is there something else that I should be doing?
Some pretty good suggestions, but I don't believe they address the underlying issue: canceling a background task.
Unfortunately, when using BackgroundWorker, termination of your task depends on the task itself. The only way your while loop will terminate, is if your background task checks its Cancel property and returns or breaks from its current process.
Example Base
For example, consider
private readonly BackgroundWorker worker = new BackgroundWorker ();
public void SomeFormEventForStartingBackgroundTask ()
{
worker.DoWork += BackgroundTask_HotelCalifornia;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync ();
}
// semantically, you want to perform this task for lifetime of
// application, you may even expect that calling CancelAsync
// will out and out abort this method - that is incorrect.
// CancelAsync will only set DoWorkEventArgs.Cancel property
// to true
private void BackgroundTask_HotelCalifornia (object sender, DoWorkEventArgs e)
{
for ( ; ;)
{
// because we never inspect e.Cancel, we can never leave!
}
}
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
worker.CancelAsync();
// [politely] wait until background task terminates
while (worker.IsBusy);
}
This is what is happening by default. Now, maybe your task isn't an infinite loop, perhaps it is just a long-running task. Either way, your main thread will block [actually it is spinning, but whatevs] until the task completes, or doesn't as the case may be.
If you have personally written and can modify the task, then you have a few options.
Example Improvement
For instance, this is a better implementation of the above example
private readonly BackgroundWorker worker = new BackgroundWorker ();
// this is used to signal our main Gui thread that background
// task has completed
private readonly AutoResetEvent isWorkerStopped =
new AutoResentEvent (false);
public void SomeFormEventForStartingBackgroundTask ()
{
worker.DoWork += BackgroundTask_HotelCalifornia;
worker.RunWorkerCompleted += BackgroundTask_Completed;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync ();
}
private void BackgroundTask_HotelCalifornia (object sender, DoWorkEventArgs e)
{
// execute until canceled
for ( ; !e.Cancel;)
{
// keep in mind, this task will *block* main
// thread until cancel flag is checked again,
// so if you are, say crunching SETI numbers
// here for instance, you could still be blocking
// a long time. but long time is better than
// forever ;)
}
}
private void BackgroundTask_Completed (
object sender,
RunWorkerCompletedEventArgs e)
{
// ok, our task has stopped, set signal to 'signaled' state
// we are complete!
isStopped.Set ();
}
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
worker.CancelAsync();
// [politely] wait until background task terminates
isStopped.WaitOne ();
}
While this is better, it's not as good as it could be. If you can be [reasonably] assured your background task will end, this may be "good enough".
However, what we [typically] want, is something like this
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
worker.CancelAsync();
// [politely] wait until background task terminates
TimeSpan gracePeriod = TimeSpan.FromMilliseconds(100);
bool isStoppedGracefully = isStopped.WaitOne (gracePeriod);
if (!isStoppedGracefully)
{
// KILL! KILL! KILL!
}
}
Alas, we cannot. BackgroundWorker does not expose any means of forceful termination. This is because it is an abstraction built on top of some hidden thread management system, one which could potentially destabalize other parts of your application if it were forcefully terminated.
The only means [that I have seen at least] to implement the above is to manage your own threading.
Example Ideal
So, for instance
private Thread worker = null;
// this time, 'Thread' provides all synchronization
// constructs required for main thread to synchronize
// with background task. however, in the interest of
// giving background task a chance to terminate gracefully
// we supply it with this cancel signal
private readonly AutoResetEvent isCanceled = new AutoResentEvent (false);
public void SomeFormEventForStartingBackgroundTask ()
{
worker = new Thread (BackgroundTask_HotelCalifornia);
worker.IsBackground = true;
worker.Name = "Some Background Task"; // always handy to name things!
worker.Start ();
}
private void BackgroundTask_HotelCalifornia ()
{
// inspect cancel signal, no wait period
//
// NOTE: so cheating here a bit, this is an instance variable
// but could as easily be supplied via parameterized thread
// start delegate
for ( ; !isCanceled.WaitOne (0);)
{
}
}
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
isCanceled.Set ();
// [politely] wait until background task terminates
TimeSpan gracePeriod = TimeSpan.FromMilliseconds(100);
bool isStoppedGracefully = worker.Join (gracePeriod);
if (!isStoppedGracefully)
{
// wipe them out, all of them.
worker.Abort ();
}
}
And that there, is a decent introduction on thread management.
Which is best suited for you? Depends on your application. It is probably best not to rock the boat, and modify your current implementation to ensure that
your background task inspects and respects the Cancel property
your main thread waits for completion, as opposed to polling
It is very important to compare and evaluate the pros and cons of each approach.
If you must control and guarantee termination of someone else's tasks, then writing a thread management system that incorporates the above may be the way to go. However you would lose out on out-of-box features like thread pooling, progress reporting, cross-thread data marshalling [worker does that, no?], and a bunch of other stuff. Not to mention, "rolling your own" is often error prone.
Anyway, hope this helps :)
Kevin Gale is correct in stating that your BackgroundWorker's DoWork handler needs to poll for CancellationPending and return if a cancellation is requested.
That being said, if this is happening when your application is shutting down, you can just ignore it safely, as well. BackgroundWorker uses a ThreadPool thread, which is, by definition, a background thread. Leaving this running will not prevent your application from terminating, and the thread will automatically be torn down when your application shuts down.
In the background worker thread you need to check the BackgroundWorker.CancellationPending flag and exit if it is true.
The CancelAsync() just sets this flag.
Or to put it another way. CancelAsync() doesn't actually cancel anything. It won't abort the thread or cause it to exit. If the worker thread is in a loop and checks the CancellationPending flag periodically it can catch the cancel request and exit.
MSDN has an example here although it doesn't use a loop in the worker routine.
This code is guaranteed to deadlock when the BGW is still running. BGW cannot complete until its RunWorkerCompleted event finished running. RunWorkerCompleted cannot run until the UI thread goes idle and runs the message loop. But the UI thread isn't idle, it is stuck in the while loop.
If you want the BGW thread to complete cleanly, you have to keep your form alive. Check this thread to see how to do that.
Try:
if (this.backgroundWorker1.IsBusy) this.backgroundWorker1.CancelAsync();