强制循环等待事件

我正在尝试编写一个遍历项目列表的方法 – 每个项目将其添加到表单中,然后等待用户输入一些数据并单击按钮。 但是我不知道我应该/可以使用什么来创建它。

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类,它似乎只适用于线程。

我怎样才能做到这一点?

如果你也使用async / await,有很多方法可以使用信号 – 你可以等待一个由点击按钮完成的任务 – 但一般来说你应该以更加事件驱动的方式考虑用户界面。

不要在此处循环使用foreach循环,而是将索引保存到正在显示项目的集合中,并在每次单击按钮时将其前进。 (当然,请记住检查是否还有更多项目要显示。)

虽然我和Jon Skeet一致同意, Mads Torgensen有一个有趣的演示文稿,演示了如何使用async/await来简化这些场景(使用Jon提到的技术)。 毕竟,与枚举器不同 – 我们可以使用索引等状态编写自己的枚举器类,但我们几乎从不这样做,而是使用迭代器块。

无论如何,这是我们谈论的async/await技术。

首先,可重复使用的部分:

 public static class Utils { public static Task WhenClicked(this Button button) { var tcs = new TaskCompletionSource(); 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 { "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(); 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/

您的应用程序是一个有限状态机 ,它响应来自用户的事件,并在所需事件到达时进入新状态,以匹配当前状态的状态转换。 多年来已经有很多尝试在代码中对此进行建模,我没有看到任何比仅使用标准事件处理更好的东西。