需要点击按钮两次才有效果
Need to click button twice to have an effect
该应用程序是一个机器控件,因此它需要访问 ui 以显示状态等。(我知道,这违背了将 UI 和工作代码分开的建议,但这就是至少现在是这样)。问题归结为:当一个按钮的事件处理程序没有完成时,另一个按钮需要被点击两次。第一次点击将焦点赋予按钮,下一次点击触发事件。
这里把问题简化到极致。有两个按钮和一个标签。停止按钮需要点击两次才能停止机器:
bool Stop = true;
private void Start_button_Click(object sender, EventArgs e)
{
RunMachine();
}
private void Stop_button_Click(object sender, EventArgs e)
{
Stop = true;
}
private void RunMachine()
{
Stop = false;
Status_label.Text = "Running";
do
{
Application.DoEvents();
Thread.Sleep(50);
}
while (!Stop);
Status_label.Text = "Stopped";
}
如何让按钮对第一次点击做出反应?
DoEvents() 不好。不要使用它。
如果您必须使用它(例如作为解决方法),那么您将增加技术债务并可能在未来支付,类似于您的情况。
更好的方法是 运行 在任务内部工作并使用取消令牌,但在您的情况下,所需的最小修改是这样的(向方法添加 async 修饰符):
while (!Stop)
{
await Task.Delay(50);
// or
await Task.Run(() => Thread.Sleep(50));
}
UI 现在应该可以响应了。
后者是模拟同步代码,把它代替Sleep
,有需要的别忘了调用modify UI.
谢谢!我不知道 Doevents 的含义,使用 async 和 await 也很简单。我添加了一个计数器来向自己展示玩具示例正在按照我的想法进行操作。为了使答案完整并帮助像我这样可能会搜索同一问题答案的其他菜鸟,这里再次提供完整示例。这按需要工作(单击停止)并且如果主窗体在没有单击停止的情况下关闭,则不会离开 RunMachine() 运行。 (我的真实应用程序在表单关闭事件中有足够的代码来防止这种情况发生,但我当然没有意识到这个陷阱。)
bool Stop = true;
private async void Start_button_Click(object sender, EventArgs e)
{
await RunMachine();
}
private void Stop_button_Click(object sender, EventArgs e)
{
Stop = true;
}
internal async Task RunMachine()
{
Status_label.Text = "started";
Stop = false;
int i=0;
do
{
await Task.Delay(500);
Status_label.Text = i.ToString();
i++;
} while (!Stop);
Status_label.Text = "Stopped";
}
该应用程序是一个机器控件,因此它需要访问 ui 以显示状态等。(我知道,这违背了将 UI 和工作代码分开的建议,但这就是至少现在是这样)。问题归结为:当一个按钮的事件处理程序没有完成时,另一个按钮需要被点击两次。第一次点击将焦点赋予按钮,下一次点击触发事件。
这里把问题简化到极致。有两个按钮和一个标签。停止按钮需要点击两次才能停止机器:
bool Stop = true;
private void Start_button_Click(object sender, EventArgs e)
{
RunMachine();
}
private void Stop_button_Click(object sender, EventArgs e)
{
Stop = true;
}
private void RunMachine()
{
Stop = false;
Status_label.Text = "Running";
do
{
Application.DoEvents();
Thread.Sleep(50);
}
while (!Stop);
Status_label.Text = "Stopped";
}
如何让按钮对第一次点击做出反应?
DoEvents() 不好。不要使用它。
如果您必须使用它(例如作为解决方法),那么您将增加技术债务并可能在未来支付,类似于您的情况。
更好的方法是 运行 在任务内部工作并使用取消令牌,但在您的情况下,所需的最小修改是这样的(向方法添加 async 修饰符):
while (!Stop)
{
await Task.Delay(50);
// or
await Task.Run(() => Thread.Sleep(50));
}
UI 现在应该可以响应了。
后者是模拟同步代码,把它代替Sleep
,有需要的别忘了调用modify UI.
谢谢!我不知道 Doevents 的含义,使用 async 和 await 也很简单。我添加了一个计数器来向自己展示玩具示例正在按照我的想法进行操作。为了使答案完整并帮助像我这样可能会搜索同一问题答案的其他菜鸟,这里再次提供完整示例。这按需要工作(单击停止)并且如果主窗体在没有单击停止的情况下关闭,则不会离开 RunMachine() 运行。 (我的真实应用程序在表单关闭事件中有足够的代码来防止这种情况发生,但我当然没有意识到这个陷阱。)
bool Stop = true;
private async void Start_button_Click(object sender, EventArgs e)
{
await RunMachine();
}
private void Stop_button_Click(object sender, EventArgs e)
{
Stop = true;
}
internal async Task RunMachine()
{
Status_label.Text = "started";
Stop = false;
int i=0;
do
{
await Task.Delay(500);
Status_label.Text = i.ToString();
i++;
} while (!Stop);
Status_label.Text = "Stopped";
}