Reputation: 377
I’m looking for a way to get an object with a running process to respond to a Windows Forms button click. Everything in this utility happens on just a single “main” Windows Application form.
The process is chewing through a bunch of files in a folder, and I want to put a "Pause" button on the form.
I could create a global variable, change its state with the button, and have the object check the state of the variable as part of its process.
But I want to write better software, and I suspect there is a way with delegates, or events, or some form of the listener design pattern, or ... , to do this more elegantly.
Thoughts?
Upvotes: 3
Views: 93
Reputation: 15055
Sounds like you're on the right lines - is your background task being invoked/called by a BackgroundWorker? - that's way to keep the GUI responsive and just pause in the loop somewhere. In your background worker have it wait on a Pause event wait handle (which will never be "set" until or if the user clicks the pause button).
Create the event like this:
KeepWorking = new EventWaitHandle(true, EventResetMode.ManualReset);
In the worker do this:
while (KeepWorking.WaitOne())
{
...
}
For your pause button set and unset the event:
KeepWorking.Reset(); // To pause it
KeepWorking.Set(); // To unpause it
Upvotes: 0
Reputation: 67948
The best way to handle this is to put the long-running process on a background thread. This can be done with a BackgroundWorker
, and is my class of choice in a Windows application. Why? Because when reporting progress through it (e.g. changing the state of a button) it handles the thread switching to the UI thread automatically.
Here is an example of how it works. Let's assume you have a private class field for the BackgroundWorker
:
private BackgroundWorker _worker = new BackgroundWorker();
In the constructor we need to setup a few things:
_worker.WorkerReportsProgress = true;
_worker.DoWork += DoWork;
_worker.ProgressChanged += ProgressChanged;
_worker.RunWorkerCompleted += RunWorkerCompleted;
Okay, so now setting up the handlers:
private void DoWork(object sender, DoWorkEventArgs e)
{
// do the work here
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// e.UserState is a value that you can pass anything to
// like a string for a status label
// e.ProgressPercentage is an integer that you specify letting
// this event know what step the process is at - this is well
// used for a progress bar
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// e.Result is a value that you can pass anything to
// like a reference to an object with the finished product
// or whatever makes sense in your case
}
Finally, to run the background process, just do this:
_worker.RunWorkerAsync();
and to report progress, while in the DoWork
handler, do this:
// NOTE: make sure to pass AT LEAST a value of 1 for the percentage
// or the event handler will NEVER fire. Further, someValue can be anything
// an instance of an object with data or just a string, or nothing for that
// matter - it's up to you
_worker.ReportProgress(1, someValue);
Upvotes: 1