Breaking from a loop with button click - C#
Use a background worker to do your work. You can use the cancellation feature to break out of it when you're done. Your loop as you have it will block the UI thread when executed syncronously, which is why your GUI becomes unresponsive. Note if you do any interaction with the UI in the do work delegate, you need to marshal back onto the UI thread (via invoke for example).
private BackgroundWorker _worker = null;
private void goButton_Click(object sender, EventArgs e)
{
_worker = new BackgroundWorker();
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
if (_worker.CancellationPending)
break;
Console.WriteLine("Hello, world");
} while (true);
});
_worker.RunWorkerAsync();
goButton.Enabled = false;
stopButton.Enabled = true;
}
private void stopButton_Click(object sender, EventArgs e)
{
stopButton.Enabled = false;
goButton.Enabled = true;
_worker.CancelAsync();
}
Update 2019:
BackgroundWorker
is now largely obsolete, replaced by the async/await
feature in later versions of C# which is easier to use. Here is an example of how to achieve the same thing using that feature:
private CancellationTokenSource _canceller;
private async void goButton_Click(object sender, EventArgs e)
{
goButton.Enabled = false;
stopButton.Enabled = true;
_canceller = new CancellationTokenSource();
await Task.Run(() =>
{
do
{
Console.WriteLine("Hello, world");
if (_canceller.Token.IsCancellationRequested)
break;
} while (true);
});
_canceller.Dispose();
goButton.Enabled = true;
stopButton.Enabled = false;
}
private void stopButton_Click(object sender, EventArgs e)
{
_canceller.Cancel();
}
There's one important thing to remember:
While your code is being executed, the user cannot interact with your user interface.
That means: You first need to exit the loop (i.e. return from the Spam
method), and then the user can click Button2.
That's a hard truth, because it means you cannot write the code in the way you wanted to. Fortunately, there are a few ways to work around that:
Don't use a loop. Use some kind of timer to do the "spamming". Button1 starts the timer, Button2 stops it. What kind of timer is available depends on the user interface library you use (WinForms has a
Timer
, WPF has aDispatcherTimer
).Do the "spamming" in a background thread. This will allow your user interface to stay responsive, and you can communicate with the background thread, for example, by setting a
volatile Boolean
. This, however, is an advanced topic (and can quickly lead to complex synchronization issues), so I suggest that you try the other option first.