强制循环等待事件
Force loop to wait for an event
我正在尝试编写一种遍历项目列表的方法 - 将每个项目添加到表单中,然后等待用户输入一些数据并单击按钮。但是我不知道我 should/could 是用什么来创建这个的。
foreach (string s in List)
{
txtName.Text = s;
//wait for button click...
// When the user is ready, they click the button to continue the loop.
}
到目前为止我只找到了EventWaitHandle
class,它似乎只适用于线程。
我怎样才能做到这一点?
如果您还使用 async/await,有 种使用信令执行此操作的方法 - 例如,您可以等待通过单击按钮完成的任务- 但总的来说,您应该以更加事件驱动的方式来考虑用户界面。
不要让您的 foreach
在此处循环,而是在要显示的项目的集合中保留一个索引,并在每次单击按钮时推进它。 (当然,请记住检查是否有 个要显示的项目。)
虽然我大体同意 Jon Skeet,但 Mads Torgensen 做了一个有趣的演讲,展示了 async/await
如何能够用于简化此类场景(使用 Jon 提到的技术)。毕竟,这与枚举器不一样——我们可以使用索引等状态编写自己的枚举器 class,但我们几乎从不这样做,而是使用迭代器块。
无论如何,这就是我们正在谈论的 async/await
技术。
首先,可重复使用的部分:
public static class Utils
{
public static Task WhenClicked(this Button button)
{
var tcs = new TaskCompletionSource<object>();
EventHandler onClick = null;
onClick = (sender, e) =>
{
button.Click -= onClick;
tcs.TrySetResult(null);
};
button.Click += onClick;
return tcs.Task;
}
}
以及您使用它的代码(请注意,您需要将您的方法标记为 async
)
foreach (string s in List)
{
txtName.Text = s;
await yourButton.WhenClicked();
}
将所有内容放在一起的示例测试:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Samples
{
static class Test
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form();
var txtName = new TextBox { Parent = form, Top = 8, Left = 8 };
var buttonNext = new Button { Parent = form, Top = txtName.Bottom + 8, Left = 8, Text = "Next" };
form.Load += async (sender, e) =>
{
var List = new List<string> { "A", "B", "C", "D " };
foreach (string s in List)
{
txtName.Text = s;
await buttonNext.WhenClicked();
}
txtName.Text = "";
buttonNext.Enabled = false;
};
Application.Run(form);
}
}
public static class Utils
{
public static Task WhenClicked(this Button button)
{
var tcs = new TaskCompletionSource<object>();
EventHandler onClick = null;
onClick = (sender, e) =>
{
button.Click -= onClick;
tcs.TrySetResult(null);
};
button.Click += onClick;
return tcs.Task;
}
}
}
我和 Jon Skeet 在这方面是一致的,试图强制 UI 框架进入一种不正常的工作方式,因为它通常会导致问题。即使它能工作,你的代码也很难被其他人理解,因为它会以一种不寻常的方式工作。
Lisp 中的某些 Web 框架的工作方式使页面网页显示看起来像方法调用。我还没有在 .NET 中看到任何这样做的东西。
我的第一个想法是让你在一个循环中调用 Application.DoEvents() 检查你的按钮回调设置的标志。但是,您的应用程序将在无所事事时使用 100% 的 CPU 等待。参见 http://blog.codinghorror.com/is-doevents-evil/
您的应用程序是一个 finite state machine,它响应来自用户的事件,并在所需事件到达以匹配当前状态的状态转换时进入新状态。多年来,已经有很多尝试在代码中对此进行建模,none 我看到已经提出了比仅使用标准事件处理更好的方法。
我正在尝试编写一种遍历项目列表的方法 - 将每个项目添加到表单中,然后等待用户输入一些数据并单击按钮。但是我不知道我 should/could 是用什么来创建这个的。
foreach (string s in List)
{
txtName.Text = s;
//wait for button click...
// When the user is ready, they click the button to continue the loop.
}
到目前为止我只找到了EventWaitHandle
class,它似乎只适用于线程。
我怎样才能做到这一点?
如果您还使用 async/await,有 种使用信令执行此操作的方法 - 例如,您可以等待通过单击按钮完成的任务- 但总的来说,您应该以更加事件驱动的方式来考虑用户界面。
不要让您的 foreach
在此处循环,而是在要显示的项目的集合中保留一个索引,并在每次单击按钮时推进它。 (当然,请记住检查是否有 个要显示的项目。)
虽然我大体同意 Jon Skeet,但 Mads Torgensen 做了一个有趣的演讲,展示了 async/await
如何能够用于简化此类场景(使用 Jon 提到的技术)。毕竟,这与枚举器不一样——我们可以使用索引等状态编写自己的枚举器 class,但我们几乎从不这样做,而是使用迭代器块。
无论如何,这就是我们正在谈论的 async/await
技术。
首先,可重复使用的部分:
public static class Utils
{
public static Task WhenClicked(this Button button)
{
var tcs = new TaskCompletionSource<object>();
EventHandler onClick = null;
onClick = (sender, e) =>
{
button.Click -= onClick;
tcs.TrySetResult(null);
};
button.Click += onClick;
return tcs.Task;
}
}
以及您使用它的代码(请注意,您需要将您的方法标记为 async
)
foreach (string s in List)
{
txtName.Text = s;
await yourButton.WhenClicked();
}
将所有内容放在一起的示例测试:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Samples
{
static class Test
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form();
var txtName = new TextBox { Parent = form, Top = 8, Left = 8 };
var buttonNext = new Button { Parent = form, Top = txtName.Bottom + 8, Left = 8, Text = "Next" };
form.Load += async (sender, e) =>
{
var List = new List<string> { "A", "B", "C", "D " };
foreach (string s in List)
{
txtName.Text = s;
await buttonNext.WhenClicked();
}
txtName.Text = "";
buttonNext.Enabled = false;
};
Application.Run(form);
}
}
public static class Utils
{
public static Task WhenClicked(this Button button)
{
var tcs = new TaskCompletionSource<object>();
EventHandler onClick = null;
onClick = (sender, e) =>
{
button.Click -= onClick;
tcs.TrySetResult(null);
};
button.Click += onClick;
return tcs.Task;
}
}
}
我和 Jon Skeet 在这方面是一致的,试图强制 UI 框架进入一种不正常的工作方式,因为它通常会导致问题。即使它能工作,你的代码也很难被其他人理解,因为它会以一种不寻常的方式工作。
Lisp 中的某些 Web 框架的工作方式使页面网页显示看起来像方法调用。我还没有在 .NET 中看到任何这样做的东西。
我的第一个想法是让你在一个循环中调用 Application.DoEvents() 检查你的按钮回调设置的标志。但是,您的应用程序将在无所事事时使用 100% 的 CPU 等待。参见 http://blog.codinghorror.com/is-doevents-evil/
您的应用程序是一个 finite state machine,它响应来自用户的事件,并在所需事件到达以匹配当前状态的状态转换时进入新状态。多年来,已经有很多尝试在代码中对此进行建模,none 我看到已经提出了比仅使用标准事件处理更好的方法。