Вырыв из цикла с нажатием кнопки - С#
У меня есть вопрос, касающийся зацикливания с событием нажатия кнопки, я перепробовал много методов и искал много страниц в поисках простого ответа за прошедший час, но правда в том, что каждый ответ просто выглядит как инопланетный код, вероятно, потому что я все еще очень новый для разработки.
Вот упрощенная версия того, что я пытаюсь сделать:
private string Message = "Hello";
private void Spam(bool loop)
{
if (loop == true)
{
while (loop == true)
{
MessageBox.Show(Message);
}
}
else { MessageBox.Show("Spamming has stopped !! "); }
}
private void button1_Click(object sender, EventArgs e)
{
Spam(true);
}
private void button2_Click(object sender, EventArgs e)
{
Spam(false);
}
Очевидно, что это не мой API, или изобретать было бы бесполезно, однако сам код длинный, и вы, ребята, всегда спрашиваете "соответствующий код" (без неуважения), так что это так.
Моя проблема: выход из цикла спама при нажатии кнопки 2, код для меня выглядит достаточно прилично для API, чтобы понять, но каждый раз, когда нажимается кнопка 1, API зависает.
Ответы
Ответ 1
Используйте фонового работника, чтобы делать свою работу. Вы можете использовать функцию отмены, чтобы выйти из нее, когда вы закончите. Ваш цикл, если он у вас есть, будет блокировать поток пользовательского интерфейса при синхронном выполнении, поэтому ваш графический интерфейс перестает отвечать на запросы. Обратите внимание, что если вы выполняете какое-либо взаимодействие с пользовательским интерфейсом в делегате do work, вам необходимо вернуться обратно в поток пользовательского интерфейса (например, с помощью invoke).
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();
}
Обновление 2019: BackgroundWorker
настоящее время в значительной степени устарел и заменен функцией async/await
в более поздних версиях С#, которая проще в использовании. Вот пример того, как добиться того же, используя эту функцию:
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();
}
Ответ 2
Есть одна важная вещь, которую нужно помнить:
Пока ваш код выполняется, пользователь не может взаимодействовать с вашим пользовательским интерфейсом.
Это означает: вам сначала нужно выйти из цикла (т.е. вернуться из метода Spam
), а затем пользователь может нажать кнопку Button2.
Это твердая правда, потому что это означает, что вы не можете написать код так, как вы хотели. К счастью, есть несколько способов обойти это:
-
Не используйте цикл. Используйте какой-то таймер, чтобы выполнить "спам". Button1 запускает таймер, Button2 останавливает его. Какой тип таймера доступен, зависит от используемой вами библиотеки пользовательского интерфейса (у WinForms есть Timer
, WPF имеет DispatcherTimer
).
-
Сделайте "спам" в фоновом потоке. Это позволит вашему пользовательскому интерфейсу оставаться отзывчивым, и вы можете общаться с фоновым потоком, например, установив volatile Boolean
. Это, однако, расширенная тема (и может быстро привести к сложным проблемам синхронизации), поэтому я предлагаю вам сначала попробовать другой вариант.
Ответ 3
Взгляните на эту концепцию:
private bool loop = false;
private void Start()
{
loop = true;
Spam("Some Message??");
}
private void Spam(string message)
{
while (loop)
{
MessageBox.Show("This API is not original");
}
}
private void button1_Click(object sender, EventArgs e)
{
loop = true;
}
private void button2_Click(object sender, EventArgs e)
{
loop = false;
}
Однако пользователь не сможет нажать кнопку, если MessageBox продолжает выскакивать, поскольку он занимает основной поток пользовательского интерфейса. Чтобы предотвратить это, вы можете использовать BackgroundWorker или запустить новый поток.
Ответ 4
При нажатии кнопки1 вызывается метод спама и начинается цикл. Когда вы нажимаете кнопку2, вызывается метод спама, но это не то же самое. Это второе выполнение, поэтому оно проверит условие и не войдет в цикл, но цикл на первом подоконнике будет запущен.
Вы должны использовать флаг, и цикл должен использовать этот флаг, чтобы определить, должен ли он работать. Он должен выглядеть примерно так:
bool run = false;
string message = "This API is not original";
private void Spam()
{
while (run == true)
{
MessageBox.Show(message);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
message = "Hellooo";
flag = true;
Spam();
}
private void button2_Click(object sender, EventArgs e)
{
flag = false;
}