C# - 让 UI 像无限循环一样运行,直到按下按钮
C# - Make UI function during like a infinite loop until button pressed
我在 C# 中编写了一个代码,其中我从 Access 数据库中提取了一些记录,但是我需要在进行下一次迭代时依赖于单击按钮。我尝试了一些 Thread 或 Tasks ,但它没有用,因为它阻止了 UI 我需要它被看到和点击。
代码如下:
bool nextClick = false ;
while (readerSelect.Read())
{
// show the correct panel
if (string.Compare(readerSelect[2].ToString(), "P1") == 0)
{
// panel with type 1
textBoxP1Text1.Text = readerSelect[3].ToString();
textBoxP1Text2.Text = readerSelect[4].ToString();
pictureBoxP1Image.ImageLocation = readerSelect[6].ToString();
}
else
{
// panel with type 2
textBoxP1Text2.Text = readerSelect[5].ToString();
}
//this while need to be kind of infinite so the interation can't be processed and
//so when i need to change iteration i click the buttonNext
while (!nextClick) {
startWhile:;
MethodInvoker mi = delegate () {
if (nextClick)
{
Application.DoEvents();
// System.Windows.Forms.Application.Run();
}
};
this.Invoke(mi);
//break;
goto startWhile;
}
private void buttonNext_Click(object sender, EventArgs e)
{
// click on the next button
nextClick = true;
}
您可以在异步任务中使用信号量,在每次单击时让按钮 Release
使用它,并让 while 循环在每次通过时等待它。这是一个简单的示例,使用添加了 button1
和 label1
的表单:
public partial class Form1 : Form
{
private readonly SemaphoreSlim signal = new SemaphoreSlim(0, int.MaxValue);
public Form1()
{
this.InitializeComponent();
this.RunLoop();
}
private async void RunLoop()
{
var i = 0;
while (true)
{
this.label2.Text = $"Enqueued: {this.signal.CurrentCount}";
await this.signal.WaitAsync(); // Wait button click async
await Task.Delay(1000); // Simulate work
this.label1.Text = $"Completed: {++i}";
}
}
private void button1_Click(object sender, EventArgs e)
{
this.signal.Release();
this.label2.Text = $"Enqueued: {this.signal.CurrentCount + 1}";
// Or if you want to limit the # people can queue up, then put this whole
// thing in an `if (signal.CurrentCount < myLimit)` block, and optionally
// disable the button once limit has been reached, and re-enable it right
// before the `WaitAsync` call above.
}
}
虽然 Dax Fohl 的回答有效,但您的设计似乎有问题。我认为您在 Form
class.
中执行了过多的业务逻辑,这违反了 Single Responsibility Principle
我建议将业务逻辑分解成它自己的 class。然后,无需 运行 遍历循环中的所有内容,您只需让按钮单击事件处理下一条记录并显示结果。这是我的意思的一个例子:
public partial class Form1 : Form
{
private readonly DataProcessor dataProcessor = new DataProcessor();
public Form1()
{
this.InitializeComponent();
}
private void button1Next_Click(object sender, EventArgs e)
{
this.buttonNext.Enabled = false;
this.ProcessNext();
}
private async void ProcessNext()
{
string s = await this.dataProcessor.ProcessNext();
this.textBoxP1Text1.Text = s;
this.buttonNext.Enabled = true;
}
}
public class DataProcessor
{
private readonly Random r = new Random(); // Or reader or whatever.
public async Task<string> ProcessNext() // Just using `string` as an example.
{
await Task.Delay(1000);
return this.r.Next().ToString();
}
}
我觉得这样以后会更容易理解,也更容易维护。当一个新的团队成员看到信号量的东西(或你未来的自己)时,很难 understand/remember 那些东西的意义是什么。在这里,您只有一个本地函数,它只做一件事并且很容易理解。
我在 C# 中编写了一个代码,其中我从 Access 数据库中提取了一些记录,但是我需要在进行下一次迭代时依赖于单击按钮。我尝试了一些 Thread 或 Tasks ,但它没有用,因为它阻止了 UI 我需要它被看到和点击。
代码如下:
bool nextClick = false ;
while (readerSelect.Read())
{
// show the correct panel
if (string.Compare(readerSelect[2].ToString(), "P1") == 0)
{
// panel with type 1
textBoxP1Text1.Text = readerSelect[3].ToString();
textBoxP1Text2.Text = readerSelect[4].ToString();
pictureBoxP1Image.ImageLocation = readerSelect[6].ToString();
}
else
{
// panel with type 2
textBoxP1Text2.Text = readerSelect[5].ToString();
}
//this while need to be kind of infinite so the interation can't be processed and
//so when i need to change iteration i click the buttonNext
while (!nextClick) {
startWhile:;
MethodInvoker mi = delegate () {
if (nextClick)
{
Application.DoEvents();
// System.Windows.Forms.Application.Run();
}
};
this.Invoke(mi);
//break;
goto startWhile;
}
private void buttonNext_Click(object sender, EventArgs e)
{
// click on the next button
nextClick = true;
}
您可以在异步任务中使用信号量,在每次单击时让按钮 Release
使用它,并让 while 循环在每次通过时等待它。这是一个简单的示例,使用添加了 button1
和 label1
的表单:
public partial class Form1 : Form
{
private readonly SemaphoreSlim signal = new SemaphoreSlim(0, int.MaxValue);
public Form1()
{
this.InitializeComponent();
this.RunLoop();
}
private async void RunLoop()
{
var i = 0;
while (true)
{
this.label2.Text = $"Enqueued: {this.signal.CurrentCount}";
await this.signal.WaitAsync(); // Wait button click async
await Task.Delay(1000); // Simulate work
this.label1.Text = $"Completed: {++i}";
}
}
private void button1_Click(object sender, EventArgs e)
{
this.signal.Release();
this.label2.Text = $"Enqueued: {this.signal.CurrentCount + 1}";
// Or if you want to limit the # people can queue up, then put this whole
// thing in an `if (signal.CurrentCount < myLimit)` block, and optionally
// disable the button once limit has been reached, and re-enable it right
// before the `WaitAsync` call above.
}
}
虽然 Dax Fohl 的回答有效,但您的设计似乎有问题。我认为您在 Form
class.
我建议将业务逻辑分解成它自己的 class。然后,无需 运行 遍历循环中的所有内容,您只需让按钮单击事件处理下一条记录并显示结果。这是我的意思的一个例子:
public partial class Form1 : Form
{
private readonly DataProcessor dataProcessor = new DataProcessor();
public Form1()
{
this.InitializeComponent();
}
private void button1Next_Click(object sender, EventArgs e)
{
this.buttonNext.Enabled = false;
this.ProcessNext();
}
private async void ProcessNext()
{
string s = await this.dataProcessor.ProcessNext();
this.textBoxP1Text1.Text = s;
this.buttonNext.Enabled = true;
}
}
public class DataProcessor
{
private readonly Random r = new Random(); // Or reader or whatever.
public async Task<string> ProcessNext() // Just using `string` as an example.
{
await Task.Delay(1000);
return this.r.Next().ToString();
}
}
我觉得这样以后会更容易理解,也更容易维护。当一个新的团队成员看到信号量的东西(或你未来的自己)时,很难 understand/remember 那些东西的意义是什么。在这里,您只有一个本地函数,它只做一件事并且很容易理解。