Brian Wren
Brian Wren

Reputation: 377

Cause Object to Respond to Button Click

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

Answers (2)

noelicus
noelicus

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

Mike Perrenoud
Mike Perrenoud

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

Related Questions