using Thread.Sleep
I manage to make a pause inside a loop but it has the disadvantage of freezing my program while the pause lasts. The purpose of my program is to start a loop when a button is clicked and to stop this loop when another button is clicked. Here is the code I wrote:
private void startRPLoop_Click(object sender, EventArgs e)
{
timer1.Interval = 1000;
timer1.Enabled = true;
}
private void stopRPLoop_Click(object sender, EventArgs e)
{
timer1.Interval = 1000;
timer1.Enabled = false;
}
private void timer1_Tick(object sender, EventArgs e)
{
if (timer1.Enabled == true)
{
GlobalRPValue = 500;
WantedLevel = 1;
Thread.Sleep(1000);
WantedLevel = 0;
Thread.Sleep(1000);
}
else
{
GlobalRPValue = 1;
WantedLevel = 0;
}
}
I thought of creating a Task
so I could use await Task.Delay();
which will allow me to start the loop and make pauses without my program being suspended because of Thread.Sleep
but I don't know how to go about it.
I hope I have been precise enough because I am new to C# and thank you for your help :)
CodePudding user response:
Your question is How to pause inside a loop without using Thread.Sleep?. You posted some sample code that uses System.Windows.Forms.Timer
but when I worked it out using Timer
it required more complicated code. This answer proposes a simpler solution that (based on our conversation) does what you want without using Timer
. It runs a loop when the button is clicked and toggles the WantedLevel
between 0 and 1 once per second without freezing your UI.
The "Button" is actually a checkbox with Appearance
=Button
. Clicking it will toggle the checked state and when toggled on, it starts a loop. First, the onTick
method sets WantedLevel to 1
for a duration of 1 second before returning. Then it will await
an additional 1-second delay before repeating the process.
CancellationTokenSource _cts = null;
private async void checkBoxLoop_CheckedChanged(object sender, EventArgs e)
{
if(checkBoxLoop.Checked)
{
labelGlobalRPValue.Text = "GlobalRPValue=500";
textBoxConsole.Text = $"{DateTime.Now.ToString(@"mm:ss")}: Start clicked{Environment.NewLine}";
textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: {labelGlobalRPValue.Text} {Environment.NewLine}");
_cts = new CancellationTokenSource();
while (checkBoxLoop.Checked)
{
try {
await onTick(_cts.Token);
await Task.Delay(1000, _cts.Token);
}
catch(TaskCanceledException)
{
break;
}
}
ResetDefaults();
}
else
{
textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: Stop clicked{Environment.NewLine}");
_cts?.Cancel();
}
}
The onTick
handler is marked async
which allows the Task.Delay
to be awaited. Other than that it's quite simple and tries to follow the essence of the handler you posted.
private async Task onTick(CancellationToken token)
{
labelWantedLevel.Text = "WantedLevel=1";
textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: {labelWantedLevel.Text} {Environment.NewLine}");
await Task.Delay(1000, token);
labelWantedLevel.Text = "WantedLevel=0";
textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: {labelWantedLevel.Text} {Environment.NewLine}");
}
When the checkbox state toggles off, it cancels the current Task.Delay
using the CancellationTokenSource
which causes the loop to exit. The ResetDefaults()
method is called to restore default values for WantedLevel
and GlobalRPValue
.
private void ResetDefaults()
{
labelGlobalRPValue.Text = "GlobalRPValue=1";
labelWantedLevel.Text = "WantedLevel=0";
textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: Cancelled (reset defaults) {Environment.NewLine}");
textBoxConsole.AppendText($"{labelGlobalRPValue.Text} {Environment.NewLine}");
textBoxConsole.AppendText($"{labelWantedLevel.Text} {Environment.NewLine}");
}
EDITS TO CONFORM TO ORIGINAL POST PER COMMENT
Handle Buttons
private bool _checkBoxLoop_Checked = false;
private void startRPLoop_Click(object sender, EventArgs e)
{
_checkBoxLoop_Checked = true;
checkBoxLoop_CheckedChanged(sender, e);
}
private void stopRPLoop_Click(object sender, EventArgs e)
{
_checkBoxLoop_Checked = false;
checkBoxLoop_CheckedChanged(sender, e);
}
Enable/Disable buttons for operational safety
private async void checkBoxLoop_CheckedChanged(object sender, EventArgs e)
{
stopRPLoop.Enabled = _checkBoxLoop_Checked; // Added
startRPLoop.Enabled = !stopRPLoop.Enabled; // Added
if (_checkBoxLoop_Checked) // use member variable instead of checkbox state
{
labelGlobalRPValue.Text = "GlobalRPValue=500";
textBoxConsole.Text = $"{DateTime.Now.ToString(@"mm:ss")}: Start clicked{Environment.NewLine}";
textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: {labelGlobalRPValue.Text} {Environment.NewLine}");
_cts = new CancellationTokenSource();
while (_checkBoxLoop_Checked)
{
try {
await onTick(_cts.Token);
await Task.Delay(1000, _cts.Token);
}
catch(TaskCanceledException)
{
break;
}
}
ResetDefaults();
}
else
{
textBoxConsole.AppendText($"{DateTime.Now.ToString(@"mm:ss")}: Stop clicked{Environment.NewLine}");
_cts?.Cancel();
}
}